[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"academy-blogs-en-1-1-all-storing-chat-history-websocket-graphql-all--*":3,"academy-blog-translations-h9qo5h7vbko0hq3":104},{"data":4,"page":92,"perPage":92,"totalItems":92,"totalPages":92},[5],{"alt":6,"collectionId":7,"collectionName":8,"content":9,"cover_image":10,"cover_image_path":11,"created":12,"created_by":13,"expand":14,"id":99,"keywords":100,"locale":74,"published_at":101,"scheduled_at":13,"school_blog":96,"short_description":102,"status":94,"title":6,"updated":103,"updated_by":13,"slug":97,"views":98},"EP.35 Storing Chat History in WebSocket and GraphQL Subscription","sclblg987654321","school_blog_translations","\u003Ch2>Why Store Chat History?\u003C\u002Fh2>\u003Cp>Storing chat history allows users to retrieve old messages, view message history, and operate the chat system continuously without losing data when the application is closed. It also enables the system to display past messages, such as:\u003C\u002Fp>\u003Cul>\u003Cli>Fetching recent messages when a user joins a conversation\u003C\u002Fli>\u003Cli>Loading historical chat messages\u003C\u002Fli>\u003Cli>Saving messages to the database for later retrieval\u003C\u002Fli>\u003C\u002Ful>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch3>Structure of the Chat History Storage System\u003C\u002Fh3>\u003Col>\u003Cli>WebSocket Server - Receives and sends messages via WebSocket\u003C\u002Fli>\u003Cli>GraphQL API - Manages message sending and receiving, and stores data\u003C\u002Fli>\u003Cli>Database (PostgreSQL \u002F MongoDB) - Used to store chat history\u003C\u002Fli>\u003Cli>GraphQL Subscription - Sends new messages to users in real-time\u003C\u002Fli>\u003C\u002Fol>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch3>Install Necessary Libraries\u003C\u002Fh3>\u003Cpre>\u003Ccode class=\"language-plaintext\">go get github.com\u002F99designs\u002Fgqlgen\ngo get github.com\u002Fgorilla\u002Fwebsocket\ngo get github.com\u002Fjmoiron\u002Fsqlx\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch3>Database Setup for Storing Chat History\u003C\u002Fh3>\u003Cp>File: schema.sql\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext\">CREATE TABLE messages (\n    id SERIAL PRIMARY KEY,\n    content TEXT NOT NULL,\n    sender TEXT NOT NULL,\n    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch3>Creating GraphQL Schema for Storing Chat History\u003C\u002Fh3>\u003Cp>File: schema.graphql\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext\">type Query {\n  messages: [Message!]!\n}\n\ntype Mutation {\n  sendMessage(content: String!, sender: String!): Message!\n}\n\ntype Subscription {\n  messageAdded: Message!\n}\n\ntype Message {\n  id: ID!\n  content: String!\n  sender: String!\n  timestamp: String!\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch3>Creating Resolver to Connect to the Database\u003C\u002Fh3>\u003Cp>File: resolver.go\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext\">package main\n\nimport (\n    \"context\"\n    \"database\u002Fsql\"\n    \"fmt\"\n    \"sync\"\n    \"time\"\n\n    _ \"github.com\u002Flib\u002Fpq\"\n)\n\ntype Message struct {\n    ID        int       `json:\"id\"`\n    Content   string    `json:\"content\"`\n    Sender    string    `json:\"sender\"`\n    Timestamp time.Time `json:\"timestamp\"`\n}\n\ntype Resolver struct {\n    mu          sync.Mutex\n    db          *sql.DB\n    subscribers map[string]chan Message\n}\n\nfunc NewResolver(db *sql.DB) *Resolver {\n    return &amp;Resolver{\n        db:          db,\n        subscribers: make(map[string]chan Message),\n    }\n}\n\nfunc (r *Resolver) Query_messages(ctx context.Context) ([]Message, error) {\n    rows, err := r.db.Query(\"SELECT id, content, sender, timestamp FROM messages ORDER BY timestamp DESC LIMIT 50\")\n    if err != nil {\n        return nil, err\n    }\n    defer rows.Close()\n\n    var messages []Message\n    for rows.Next() {\n        var msg Message\n        if err := rows.Scan(&amp;msg.ID, &amp;msg.Content, &amp;msg.Sender, &amp;msg.Timestamp); err != nil {\n            return nil, err\n        }\n        messages = append(messages, msg)\n    }\n    return messages, nil\n}\n\nfunc (r *Resolver) Mutation_sendMessage(ctx context.Context, content string, sender string) (Message, error) {\n    var message Message\n    err := r.db.QueryRow(\"INSERT INTO messages (content, sender) VALUES ($1, $2) RETURNING id, content, sender, timestamp\", content, sender).\n        Scan(&amp;message.ID, &amp;message.Content, &amp;message.Sender, &amp;message.Timestamp)\n    if err != nil {\n        return Message{}, err\n    }\n\n    r.mu.Lock()\n    for _, ch := range r.subscribers {\n        ch &lt;- message\n    }\n    r.mu.Unlock()\n    return message, nil\n}\n\nfunc (r *Resolver) Subscription_messageAdded(ctx context.Context) (&lt;-chan Message, error) {\n    id := fmt.Sprintf(\"%d\", len(r.subscribers))\n    r.mu.Lock()\n    ch := make(chan Message)\n    r.subscribers[id] = ch\n    r.mu.Unlock()\n\n    go func() {\n        &lt;-ctx.Done()\n        r.mu.Lock()\n        delete(r.subscribers, id)\n        close(ch)\n        r.mu.Unlock()\n    }()\n    return ch, nil\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch3>Creating WebSocket Server and Connecting to the Database\u003C\u002Fh3>\u003Cp>File: server.go\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext\">package main\n\nimport (\n    \"database\u002Fsql\"\n    \"log\"\n    \"net\u002Fhttp\"\n    \"github.com\u002F99designs\u002Fgqlgen\u002Fgraphql\u002Fhandler\"\n    \"github.com\u002F99designs\u002Fgqlgen\u002Fgraphql\u002Fplayground\"\n)\n\nfunc main() {\n    db, err := sql.Open(\"postgres\", \"user=postgres password=secret dbname=chat sslmode=disable\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer db.Close()\n\n    resolver := NewResolver(db)\n    srv := handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))\n\n    http.Handle(\"\u002F\", playground.Handler(\"GraphQL Playground\", \"\u002Fquery\"))\n    http.Handle(\"\u002Fquery\", srv)\n\n    log.Println(\"Chat Server with Database running at http:\u002F\u002Flocalhost:8080\")\n    log.Fatal(http.ListenAndServe(\":8080\", nil))\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch4>Challenge!\u003C\u002Fh4>\u003Cp>Try adding features for searching old messages and filtering messages by sender in your chat system.\u003C\u002Fp>\u003Chr>\u003Ch4>Next EP\u003C\u002Fh4>\u003Cp>In EP.36, we will add User Authentication to the WebSocket Chat to control access permissions 🚀\u003C\u002Fp>","42_1_wffeast8fs.webp","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclblg987654321\u002Fvk23g8wbz2hrzpj\u002F42_1_wffeast8fs.webp","2026-03-04 08:51:17.771Z","",{"keywords":15,"locale":68,"school_blog":78},[16,23,28,33,38,43,48,53,58,63],{"collectionId":17,"collectionName":18,"created":19,"created_by":13,"id":20,"name":21,"updated":22,"updated_by":13},"sclkey987654321","school_keywords","2026-03-04 08:48:07.088Z","brfbypclggbbkcx","WebSocket API","2026-04-10 16:13:40.594Z",{"collectionId":17,"collectionName":18,"created":24,"created_by":13,"id":25,"name":26,"updated":27,"updated_by":13},"2026-03-04 08:50:52.953Z","aqepcyhdmag8vg4","Chat Storage","2026-04-10 16:14:29.336Z",{"collectionId":17,"collectionName":18,"created":29,"created_by":13,"id":30,"name":31,"updated":32,"updated_by":13},"2026-03-04 08:51:13.649Z","wcctbc65iq5n4bj","GraphQL API","2026-04-10 16:14:34.086Z",{"collectionId":17,"collectionName":18,"created":34,"created_by":13,"id":35,"name":36,"updated":37,"updated_by":13},"2026-03-04 08:51:17.295Z","x5fbzy09361bbm5","Chat Database","2026-04-10 16:14:35.229Z",{"collectionId":17,"collectionName":18,"created":39,"created_by":13,"id":40,"name":41,"updated":42,"updated_by":13},"2026-03-04 08:46:53.342Z","5ac1xgod1ehyqva","GraphQL Subscriptions","2026-04-10 16:13:20.256Z",{"collectionId":17,"collectionName":18,"created":44,"created_by":13,"id":45,"name":46,"updated":47,"updated_by":13},"2026-03-04 08:34:00.920Z","ecac9y661or1xka","WebSocket","2026-04-10 16:08:05.227Z",{"collectionId":17,"collectionName":18,"created":49,"created_by":13,"id":50,"name":51,"updated":52,"updated_by":13},"2026-03-04 08:50:53.155Z","peza6kmj1144b0x","Chat History","2026-04-10 16:14:29.504Z",{"collectionId":17,"collectionName":18,"created":54,"created_by":13,"id":55,"name":56,"updated":57,"updated_by":13},"2026-03-04 08:47:05.949Z","caufix9o52uw4bh","Real-Time Chat","2026-04-10 16:13:23.517Z",{"collectionId":17,"collectionName":18,"created":59,"created_by":13,"id":60,"name":61,"updated":62,"updated_by":13},"2026-03-04 08:20:14.253Z","ah6lvy4x8qe08l5","Golang","2026-04-10 16:07:26.172Z",{"collectionId":17,"collectionName":18,"created":64,"created_by":13,"id":65,"name":66,"updated":67,"updated_by":13},"2026-03-04 08:20:11.547Z","ey3puyme01a9bsw","Go","2026-04-10 16:07:25.893Z",{"code":69,"collectionId":70,"collectionName":71,"created":72,"flag":73,"id":74,"is_default":75,"label":76,"updated":77},"en","pbc_1989393366","locales","2026-01-22 11:00:02.726Z","twemoji:flag-united-states","qv9c1llfov2d88z",false,"English","2026-04-10 15:42:46.825Z",{"category":79,"collectionId":80,"collectionName":81,"created":13,"expand":82,"id":96,"slug":97,"updated":13,"views":98},"wqxt7ag2gn7xcmk","pbc_2105096300","school_blogs",{"category":83},{"blogIds":84,"collectionId":85,"collectionName":86,"created":87,"created_by":13,"id":79,"image":88,"image_alt":13,"image_path":89,"label":90,"name":91,"priority":92,"publish_at":93,"scheduled_at":13,"status":94,"updated":95,"updated_by":13},[],"sclcatblg987654321","school_category_blogs","2026-03-04 08:33:53.210Z","59ty92ns80w_15oc1implw.png","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclcatblg987654321\u002Fwqxt7ag2gn7xcmk\u002F59ty92ns80w_15oc1implw.png",{"en":91,"th":91},"Golang The Series",1,"2026-03-16 04:39:38.440Z","published","2026-04-25 02:32:15.470Z","h9qo5h7vbko0hq3","storing-chat-history-websocket-graphql",292,"vk23g8wbz2hrzpj",[20,25,30,35,40,45,50,55,60,65],"2025-03-13 02:24:01.462Z","Learn how to use WebSocket and GraphQL Subscription to store chat history in a database, supporting the retrieval of past messages and real-time updates to enhance your chat system.","2026-05-06 08:38:40.425Z",{"th":97,"en":97}]