🔧 AI Nachrichten Major AI platforms go down in unprecedented simultaneous outage(03.09.2026 um 17:34 Uhr)
🔧 AI Nachrichten ChatGPT, Claude, and Grok Down? Users Report Widespread Outages(03.09.2026 um 19:14 Uhr)
🔧 AI Nachrichten OpenAI Launches GPT-6 Astra, Says We May Have Entered the AGI Era(03.09.2026 um 22:08 Uhr)
🔧 AI Nachrichten Claude Comes to CarPlay as Fifth Major AI Chatbot App(05.09.2026 um 05:31 Uhr)
🔧 AI Nachrichten OpenAI’s GPT-6 Astra Is AGI, Says NVIDIA CEO Jensen Huang(07.09.2026 um 06:31 Uhr)
🔧 AI Nachrichten Blame AI companies for Mac mini and Mac Studio shortage(31.08.2026 um 10:32 Uhr)
🔧 AI Nachrichten Major AI platforms go down in unprecedented simultaneous outage(03.09.2026 um 17:34 Uhr)
🔧 AI Nachrichten ChatGPT, Claude, and Grok Down? Users Report Widespread Outages(03.09.2026 um 19:14 Uhr)
🔧 AI Nachrichten OpenAI Launches GPT-6 Astra, Says We May Have Entered the AGI Era(03.09.2026 um 22:08 Uhr)
🔧 AI Nachrichten Claude Comes to CarPlay as Fifth Major AI Chatbot App(05.09.2026 um 05:31 Uhr)
🔧 AI Nachrichten OpenAI’s GPT-6 Astra Is AGI, Says NVIDIA CEO Jensen Huang(07.09.2026 um 06:31 Uhr)
🔧 AI Nachrichten Blame AI companies for Mac mini and Mac Studio shortage(31.08.2026 um 10:32 Uhr)

🔧 Programmierung 🕛 kürzlich 25 Min Lesezeit
0

Build a Real-Time Voting System with Strapi & Instant DB: Part 1

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht




Introduction



Applications with real-time features are a vital part of today's digital life.

Many everyday apps and services have integrated real-time features. Examples include instant messaging applications like Telegram which allows you to send and receive messages in real-time, and Google Meet, which enables you to make video calls with other people in real time.

These real-time features are very engaging and provide a great user experience.



In this tutorial, we'll explore how to build a real-time voting app with , and





Introduction to the technologies



Let's talk about the technologies we'll be using:





Strapi



Strapi is a popular headless CMS that allows us to customize the APIs it provides depending on the needs of our project and can be consumed by any frontend framework of our choice.





Instant DB



A real-time database service that makes it easy to store, manage, and sync data across multiple clients instantly. It provides real-time syncing of data across clients with minimal configuration, making it perfect for use cases like live voting, collaborative editing, and real-time messaging.





Next.js



This is our front-end framework of choice for this tutorial. It is a React-based framework with powerful features, including file-based routing, built-in CSS support, and API routes.





Prerequisites



Before we begin, make sure you have the following ready:





  1. Basic JavaScript and and npm to run Strapi and Next.js.


  2. Instant DB Account: Sign up for
    is recommended.





Step 1: Setting up Strapi 5



We'll start by creating the backend for our project using Strapi 5.

Create a central folder that will hold both the backend and frontend projects. Create a folder called voting.




CODE
mkdir voting






Then, we move into the folder:




CODE
cd voting






`



Create the Strapi project by running any of the commands below



`bash





yarn



yarn create strapi-app votes-api --quickstart





npx



npx create-strapi@latest votes-api





pnpm



pnpm create strapi votes-api

`



This command will take us through a few prompts:



`

Need to install the following packages:



Here, we'll enter our details to create a new admin user.





Next, we start creating fields for our new collection type.






  • User:


    • Type: Relation (Many-to-One)

    • Name: user

    • Reference: User (from users-permissions)





Click on Finish to proceed.





Click on Save and the server will restart due to changes our configuration made in the codebase.





Creating the Option Collection Content Type



Click on the + Create new collection type option again and enter the configuration for the Option collection.




  • Display Name: option





With that, we can click on Finish. Then click on Save to save the collection type and restart the server.





Creating the Vote Collection Content Type



Click on the + Create new collection type option again and enter the configuration for the Vote collection.




  • Display Name: vote





Click on Save to save the Vote collection type and restart the server.





Allowing API access for Authenticated users



To make authenticated API calls, we'll need to edit the Authenticated role on the Users & Permissions plugn.



Navigate to Settings > Roles > Authenticated and in the Permissions, enable all permissions for Option, Poll, and Vote.



, Poll and User at Poll creation. You can learn more about



Here's the command line equivalent, make sure to add the Authoriaztion header:



`bash

curl -X POST \

'http://localhost:1337/api/polls?populate=*' \

--header 'Accept: */*' \

--header 'User-Agent: Thunder Client (https://www.thunderclient.com)' \

--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiaWF0IjoxNzI3NjA2ODAxLCJleHAiOjE3MzAxOTg4MDF9.UudUAIcX8dMyqX-pqfRQKweQoDjqBavkjdLNYsYdQ0A' \

--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiaWF0IjoxNzI3NjA2ODAxLCJleHAiOjE3MzAxOTg4MDF9.UudUAIcX8dMyqX-pqfRQKweQoDjqBavkjdLNYsYdQ0A' \

--header 'Content-Type: application/json' \

--data-raw '{

"data": {

"question": "Can the avengers beat the Gaurdians?"

}

}'

`


Here's the response:

`json

{

"data": {

"id": 2,

"documentId": "to5k2s0211nbrsn0vchvszaj",

"question": "Can the avengers beat the Gaurdians?",

"createdAt": "2024-09-29T12:06:07.605Z",

"updatedAt": "2024-09-29T12:06:07.605Z",

"publishedAt": "2024-09-29T12:06:07.617Z",

"locale": null,

"options": [],

"votes": [],

"localizations": []

},

"meta": {}

}

`


Notice, that despite adding the populate=* query parameter, the user relation field is not included in this response. We'll fix this later, but for now, If we check it out in the Strapi Admin, we should see the user who created the poll:



- strapi.documents to find an existing vote document created by the currently authenticated user and is connected to the specified poll. If a document is found, then the user has already submitted a vote for that poll and we return an error response.



Let's see it in action.



First, we create a new vote:





Awesome!





Returning User Relation Data in Poll Response



As we mentioned before, Strapi doesn't return the user relation data by defualt when we try populating the fields using the populate=* query parameter.

We have a few methods we can try to show this user relation data, let's quickly go over them.




  1. Enabling Authenticated Access to User Permissions Plugin
    We'll have to navigate to Settings > Roles (Under Users & Permissions Plugin) > Public.



Here, we can scroll down to Users-permissions and enable count, find and findOne



but that seems to only work for entries created via the Strapi Admin dashboard.




  1. Customizing the Poll controller
    That being said, our next solution could be to customize the default Poll controller at ./src/api/poll/controllers/poll.ts:
    `ts
    // ./src/api/poll/controllers/poll.ts
    /**


    • poll controller

    • This controller extends the default functionality for the poll API.
      /
      import { factories } from "@strapi/strapi";
      // Export the customized poll controller
      export default factories.createCoreController(
      "api::poll.poll", // Define the controller for the "poll" API
      ({ strapi }) => ({
      // Custom find method that retrieves multiple polls with additional user and vote data
      async find(ctx) {
      // Call the base "find" method from the core controller
      const response = await super.find(ctx);
      // For each poll in the response data, fetch related documents with detailed user and vote information
      await Promise.all(
      response.data.map(async (poll) => {
      // Retrieve the poll document with populated fields for votes and user data
      const pollDocument = await strapi
      .documents("api::poll.poll") // Access the "poll" collection
      .findOne({
      documentId: poll.documentId, // Find by the poll's document ID
      populate: {
      votes: {
      populate: {
      option: {
      fields: ["id", "value"], // Include "id" and "value" for each option
      },
      user: {
      fields: ["id", "username", "email"], // Include "id", "username", and "email" for each user
      },
      },
      },
      user: {
      fields: ["id", "username", "email"], // Include poll creator's "id", "username", and "email"
      },
      },
      });
      // Add the user details to the poll response
      poll.user = {
      id: pollDocument.user.id,
      documentId: pollDocument.user.documentId,
      username: pollDocument.user.username,
      email: pollDocument.user.email,
      };
      // Assign the populated votes data to the poll
      poll.votes = pollDocument.votes;
      })
      );
      // Return the modified response with additional data
      return response;
      },
      // Custom findOne method to retrieve a single poll by its ID with populated user and vote information
      async findOne(ctx) {
      // Call the base "findOne" method from the core controller
      const response = await super.findOne(ctx);
      // Fetch the poll document and its associated user and vote data
      const pollDocument = await strapi.documents("api::poll.poll").findOne({
      documentId: response.data.documentId, // Use the poll's document ID to find it
      populate: {
      votes: {
      populate: {
      option: {
      fields: ["id", "value"], // Include vote option details
      },
      user: {
      fields: ["id", "username", "email"], // Include user details for each vote
      },
      },
      },
      user: {
      fields: ["id", "username", "email"], // Include poll creator's details
      },
      },
      });
      // Attach the user details to the response
      response.data.user = {
      id: pollDocument.user.id,
      documentId: pollDocument.user.documentId,
      username: pollDocument.user.username,
      email: pollDocument.user.email,
      };
      // Attach the votes to the response
      response.data.votes = pollDocument.votes;
      // Return the modified response with user and vote information
      return response;
      },
      })
      );
      `
      Here, we have a custom controller for the "
      Poll*" API, built using the createCoreController factory method.





We override two methods: find and findOne, which are responsible for fetching multiple polls and a single poll, respectively. Both methods extend the default functionality by using super , then we fetch additional details about the poll creator (user) and associated votes.



The find method iterates over all retrieved polls, retrieves user and vote data from the database, and populates this information into the poll objects. Similarly, the findOne method fetches detailed information about a specific poll's creator and its votes. By doing so, we have more comprehensive information available on the front end, such as user IDs, emails, and vote details.



In the next section, we'll dive into how we can integrate Instant DB into our API by customizing the Strapi backend.





Step 3: Setting up Instant DB



To get started with Instant DB, create a new account if you don't have one already at



You can go through the onboarding process to create your app:





To obtain your Admin secret key, navigate to the admin page by clicking on Admin at the side navigation:



. Run the following command in your terminal:



`

npm i @instantdb/admin

`



Go to your



Here's the response data:



`json

{

"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiaWF0IjoxNzI3NjA2ODAxLCJleHAiOjE3MzAxOTg4MDF9.UudUAIcX8dMyqX-pqfRQKweQoDjqBavkjdLNYsYdQ0A",

"user": {

"id": 2,

"documentId": "s0z5llgt1cuitio2bv2u7ryz",

"username": "james",

"email": "[email protected]",

"provider": "local",

"confirmed": true,

"blocked": false,

"createdAt": "2024-09-29T10:46:41.898Z",

"updatedAt": "2024-09-29T10:46:41.898Z",

"publishedAt": "2024-09-29T10:46:41.899Z",

"locale": null

},

"instantdbToken": "659f0759-c2bf-47ca-8818-380d6f2e241b"

}

`




CODE

Notice the additional `instantdbToken` property in the JSON response, we can use that to make authenticated Instant DB queries and writes in our frontend.

To login, send a POST request to `http://localhost:1337/api/auth/local` with the following body:






json

{

"identifier": "[email protected]",

"password": "Pass1234"

}




CODE
With that, we should have something like this:

![login user and instantdb token.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/login_user_and_instantdb_token_af96261574.png)

You can also see the token here as well.

### Creating Vote records on Instant DB

To create votes on Instant DB when the user submits a vote on the Strapi API, we'll use the `db.asUser()`and `transact` methods. In the `./src/api/vote/middlewares/on-vote-create.ts` file, add the following:






ts

// ./src/api/vote/middlewares/on-vote-create.ts

/**





  • on-vote-create middleware


  • This middleware executes logic when a new vote is created.

    */

    import type { Core } from "@strapi/strapi";

    import { db } from "../../..";

    import { id, tx } from "@instantdb/admin";

    export default (config, { strapi }: { strapi: Core.Strapi }) => {

    // Add your own logic here.

    return async (ctx, next) => {

    strapi.log.info("In on-vote-create middleware.");

    // ...



    // Proceed to the next middleware or controller

    await next();



    // Retrieve the document ID of the new vote from the response body

    const voteDocumentId = ctx.response.body.data.documentId;

    // Retrieve the document data from the response body

    const document = ctx.response.body.data;

    // Create a new record in the InstantDB database for the vote

    const res = await db

    .asUser({

    // Use the user's information to create the vote record

    email: user.email,

    })

    .transact(

    tx.votes[id()].update({

    // Use the user's information to create the vote record

    user: {

    documentId: user.documentId,

    username: user.username,

    email: user.email,

    },

    // Use the poll and option information from the vote document

    poll: {

    documentId: document.poll,documentId,

    question: document.poll.question,

    },

    // Use the option information from the vote document

    option: {

    documentId: document.option.documentId,

    value: document.option.value,

    },

    // Use the creation timestamp from the vote document

    createdAt: document.createdAt,

    })

    );

    console.log("🟢🟢🟢🟢 ~ instantDB record created", res);



    // ...

    };

    };








CODE


Here, we connect to Instant DB using the `db.asUser()` method, ensuring the vote is created in the database under the user’s identity. The `transact` function is used to update the votes collection on Instant DB, saving the vote’s details such as the user’s document ID, poll question, option value, and creation timestamp.

Now, if we send a request to create a vote, we should see something like this in our terminal:

![instantdb record in the command line.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/instantdb_record_in_the_command_line_eb8a85510f.png)

Great. Now, if we check our Instant DB dashboard, we should see the records we've created so far:

![all instant db records.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/all_instant_db_records_7000996eb7.png)

## Conclusion

So far, we've been able to set up our Strapi backend by creating our collection types and creating and registering custom middleware to extend Strapi 5 functionality to fit our needs.
We’ve also been able to set up Instant DB admin for creating real-time vote entries on behalf of authenticated users.

Next, we'll create the front end for our project where we'll be able to create polls, vote, and see the changes in real-time.

### Resources

- [Frontend source code on GitHub](https://github.com/miracleonyenma/votes-client)
- [Backend source code on GitHub](https://github.com/miracleonyenma/votes-api)
- [Live preview hosted on Netlify](https://tryvotes.netlify.app/)


Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
3 Quellen
GPT-6 Astra Release Today? OpenAI’s Next Major AI Model Is Almost Here
1 Quelle
Apple accuses OpenAI of destroying evidence as trade-secrets fight intensifies
1 Quelle
Major AI platforms go down in unprecedented simultaneous outage
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Build a Real-Time Voting System with Strapi & Instant DB: Part 1

Thematisch verwandte Begriffe: Build, RealTime, Voting, System · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...