Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 4776

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 61k
Author
Asked: November 27, 20242024-11-27T03:08:07+00:00 2024-11-27T03:08:07+00:00

Building Tetris using WebSocket and Svelte Stores

  • 61k

In this article I wanted to share my experience of building a Web Application that uses WebSocket connection instead of HTTP request/response. As a sample Web Application we'll be building Tetris game, because… why not?

You can checkout the full source code right away on GitHub

Key Features

  • Full Duplex. Both the client and the server can send messages at the same time. So when we send a request from frontend to backend we will not wait for a response. Instead we will subscribe to an event (Subject) that will be triggered by backend request (which can be considered as a response).
  • Synchronized Svelte Stores. When backend sends a request to frontend, all we have to do is to update a corresponding Svelte Store. Which in turn will trigger a UI update.

Tech Stack

  • Backend: NodeJS with Typescript
  • Socket Server: uWebSockets
  • Validations: Zod
  • Frontend: SvelteKit project used as a SSG
  • Styling: TailwindCSS
  • package manager: pnpm

Starting the uWebSockets (backend) server

Here's how I start our WebSocket server:

import { App, WebSocket } from 'uWebSockets.js';  const SocketServer = App({});  const startServer = () => {   console.log('starting backend server...');    SocketServer.ws<{ socket_id: string }>('/tetris', {     open: () => {       console.log('opening connection');     },     close: () => {       console.log('closing connection');     },     message: (ws: WebSocket<{ socket_id: string }>, message: ArrayBuffer, isBinary: boolean) => {       console.log('message received')     },   }).listen(8080, () => {     console.log('backend server is running on: ', 8080);   }); };  startServer(); 
Enter fullscreen mode Exit fullscreen mode

uWebSockets socket server

uWebSockets is web server written in C++. In our case we'll be using uWebSockets.js which has bindings to NodeJS. It can be used as a usual web server but the main feature is its WebSocket server. It is 10x faster than Socket.IO and 8.5x faster than Fastify. I have to say I haven't benchmarked it myself. I decided to use uWebSockets just because it feels more pure to my taste. For example it doesn't require Express it runs on it's own, also it doesn't have any additional wrappers or helper functions that “babysit” you. You just get a message and you handle it whatever the way you want it. So if you're ok with that approach and you need a faster WebSocket server then you should use uWebSockets.

Messages

Frontend and Backend will be sending each other a data which we'll be calling “messages”. A Message is a JSON object like this:

export type TMessage = {   type: MessageType;   body: any; };  export enum MessageType {   MOVE_LEFT,   MOVE_RIGHT, } 
Enter fullscreen mode Exit fullscreen mode

Depending on a Message Type our backend will be executing a corresponding handler with parameters supplied in “body” field. The function that executes handler depending on a Message Type we'll call the “MessageBroker” and it can look like this:

import { MessageType } from './message.js';  export const messageBroker = (type: MessageType, body: any) => {   switch (type) {     case MessageType.MOVE_LEFT:       {         moveLeftHandler(body);       }       break;     case MessageType.MOVE_RIGHT:       {         moveRightHandler(body);       }       break;   } };  export const moveLeftHandler = (params: any) => {   console.log('moving left'); };  export const moveRightHandler = (params: any) => {   console.log('moving right'); }; 
Enter fullscreen mode Exit fullscreen mode

Now we would like to send a request from backend to frontend. In our case we need to send the blocks of the tetris game. If user clicks left or right button we will redraw all blocks by sending an array, like this:

export const blocks: number[][] =[ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], [0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1], [1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1], ] 
Enter fullscreen mode Exit fullscreen mode

This array will be “synchronized” with a Svelte Store which has same type and will be used in UI to show this:

Image description

here's the UI source:

export const Rows = writable<number[][]>([]); 
Enter fullscreen mode Exit fullscreen mode

<div class="flex flex-col items-center border-l border-r border-b border-neutral-500">   {#each $Rows as row, indexRow}     <div class="flex items-center border-b border-neutral-200 last:border-b-0">       {#each row as column, indexColumn}         <Cell value="{column}" />       {/each}     </div>   {/each} </div> 
Enter fullscreen mode Exit fullscreen mode

In order to understand how all “other stuff” work you're welcome to checkout the full source code.

Here are some main features of the GitHub repo you need to keep in mind.

Monorepo

The project is a monorepo, containing both backend and frontend in “modules” folder.

packages:   - 'modules/*' 
Enter fullscreen mode Exit fullscreen mode

There are some advantages and disadvantages to using monorepo, the reason I used monorepo is that I share Typescript types and Zod shapes (more on this later) across both projects. So I declare all types in backend project and import them in frontend project, that way I have one source for all types.

Validating data using Zod

Zod is a TypeScript-first schema validation with static type inference.
So let's say for example we have this basic type for a User:

type TUser = {   email: string;   password: string; } 
Enter fullscreen mode Exit fullscreen mode

Now we want to know if the provided email field is a valid email and password field is at least 6 character length. Instead of writing a check function we can use a special library that does it for us. So I've chosen Zod to do that. Here's how:

const userShape = z.object({   email: z.string().email(),   password: z.string().min(6), });  const data = {   email: 'some@example.com',   password: '123456', };  const user = userShape.parse(data); 
Enter fullscreen mode Exit fullscreen mode

Now if the data is incorrect the userShape.parse function will throw an exception.
But the best feature of Zod is that it can infer types:

type TUser = z.infer<typeof userShape> 
Enter fullscreen mode Exit fullscreen mode

Now TUser is a proper Typescript type.
I'm using Zod validation and infered types for all data that goes between frontend and backend.

Final thoughts

Some of my decisions on building this WebApp might not be the best, I'm aware of that, so consider this article more like a conversation starter rather than a How-To guide.

Cheers!

sveltewebdevwebsocket
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 1k
  • Popular
  • Answers
  • Author

    How to ensure that all the routes on my Symfony ...

    • 0 Answers
  • Author

    Insights into Forms in Flask

    • 0 Answers
  • Author

    Kick Start Your Next Project With Holo Theme

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.