Skip to content

Instantly share code, notes, and snippets.

@toofishes
Created May 6, 2026 14:33
Show Gist options
  • Select an option

  • Save toofishes/ea7d6444d8194cfc421db708821e902d to your computer and use it in GitHub Desktop.

Select an option

Save toofishes/ea7d6444d8194cfc421db708821e902d to your computer and use it in GitHub Desktop.
Golang backend with a Tiptap helper sidecar
package sidecar
import (
"bytes"
"context"
"encoding/json"
"errors"
"net/http"
"os"
)
// Mark represents a text mark in TipTap editor - mirroring the Typescript type.
type Mark struct {
Type string `json:"type"`
Attrs map[string]any `json:"attrs,omitempty"`
}
// JSONContent represents the structure of the content in TipTap editor - mirroring the Typescript type.
type JSONContent struct {
Type string `json:"type,omitempty"`
Attrs map[string]any `json:"attrs,omitempty"`
Content []JSONContent `json:"content,omitempty"`
Marks []Mark `json:"marks,omitempty"`
Text *string `json:"text,omitempty"`
}
func sidecarURL() string {
sidecarUrl := "http://localhost:9889"
if envUrl, ok := os.LookupEnv("SIDECAR_URL"); ok {
sidecarUrl = envUrl
}
return sidecarUrl
}
func postSidecarJSON[Req any, Resp any](ctx context.Context, client *http.Client, path string, reqBody Req, errPrefix string) (*Resp, error) {
if client == nil {
client = http.DefaultClient
}
body, err := json.Marshal(reqBody)
if err != nil {
return nil, err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, sidecarURL()+path, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
return nil, errors.New(errPrefix + ": " + resp.Status)
}
var decoded Resp
if err := json.NewDecoder(resp.Body).Decode(&decoded); err != nil {
return nil, err
}
return &decoded, nil
}
type RenderTiptapResponse struct {
HTML string `json:"html"`
Markdown string `json:"markdown"`
}
func (c *JSONContent) RenderTiptap(ctx context.Context, client *http.Client, content *JSONContent) (*RenderTiptapResponse, error) {
if content == nil {
return nil, errors.New("content is nil")
}
return postSidecarJSON[*JSONContent, RenderTiptapResponse](
ctx, client, "/render-tiptap", content, "failed to render tiptap content")
}
type MarkdownToTiptapRequest struct {
Markdown string `json:"markdown"`
}
type MarkdownToTiptapResponse struct {
Tiptap JSONContent `json:"tiptap"`
}
func MarkdownToTiptap(ctx context.Context, client *http.Client, markdown string) (*JSONContent, error) {
reqBody := MarkdownToTiptapRequest{
Markdown: markdown,
}
convertResponse, err := postSidecarJSON[MarkdownToTiptapRequest, MarkdownToTiptapResponse](
ctx, client, "/markdown-to-tiptap", reqBody, "failed to convert markdown to tiptap content")
if err != nil {
return nil, err
}
return &convertResponse.Tiptap, nil
}
type SanitizeHTMLRequest struct {
HTML string `json:"html"`
}
type SanitizeHTMLResponse struct {
SanitizedHTML string `json:"sanitized"`
}
func SanitizeHTML(ctx context.Context, client *http.Client, html string) (string, error) {
reqBody := SanitizeHTMLRequest{
HTML: html,
}
sanitizeResponse, err := postSidecarJSON[SanitizeHTMLRequest, SanitizeHTMLResponse](
ctx, client, "/sanitize-html", reqBody, "failed to sanitize HTML")
if err != nil {
return "", err
}
return sanitizeResponse.SanitizedHTML, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment