Spekni - A recognition platform built for developer endorsements

Spekni - A recognition platform built for developer endorsements


8 min read

Spekni is a platform for recognizing developers making an impact, contributing to better open-source projects, helping other developers, creating quality tutorials, and writing helpful articles in the tech community for the sole purpose of getting them noticed by the right people.

Spekni also works as a platform for finding capable developers because we simplify the process for recruiters to find passionate developers. We know most companies find it hard to find well-experienced developers who match the skills on their resumes. Endorsements from other experienced developers can go a long way in fixing that problem.


Most developers do not seek credit for their work, tirelessly putting in a lot of effort to deliver great projects for users, clients, and other devs. Sometimes developers who constantly give back to the community are the least appreciated, and as such, they sometimes find it hard to get recognition for their work. This is what we hope to fix with Spekni ๐ŸŽ‰

UI/UX and Style Guide

We choose the color blue which signifies trust, peace, loyalty, and competence just like Hashnode. The design was done completely on Figma, and we drew inspirations from several websites like Vercel, Toptal, Peerlist, etc. Since this is a product for developers, we had to add a dark mode feature with system default theme switching. ๐Ÿ˜

How does Spekni Work?

๐Ÿ“ Login

To get started on Spekni, simply visit the login page, in this page, we've provided two sign-up mediums: GitHub and Google. This authentication is powered by NextAuthJS.

Spekni Login page

๐Ÿ“ Edit Account

After connecting your account, you'll be redirected to the edit account page, in this page users can fill up the form with their bio information, job description, social accounts, and finally, their stack/tech skills,


The skills in this section are set up in a way that they would be endorsable by another user that is fully registered and authenticated on the platform. For now, a user can have a minimum of 3 skills and a maximum of 6 skills which we might increase later. The skills input fields are limited to 40 characters because we expect keywords like JavaScript, Public Speaking, Open source, NextJS, Technical Writing, C++, PHP, etc.


Though we are still working on making this more accessible to users, so if you have suggestions or ways we can improve on it, you can drop your suggestions, and we might give you a shout-out in our changelogs if they are implemented. ๐Ÿ˜‰

๐Ÿ“ Profile

Automatically all data provided in this form are stored on the PlanetScale database. A simple configuration of the database and code will allow these skills to be endorsable. Your profile is stored in your unique username which another user can visit to see your profile, for example, you can check out my profile: https://spekni.vercel.app/eke

๐Ÿ“ Endorsements

Once you finish setting up your profile, you can now start getting your skills endorsed by other people who register on the platform. It's pretty straightforward.


๐Ÿ“ Recommendation

Users can also drop recommendations to other developers by visiting their profile and adding what they will like to say about the developer in the input box. Here you can talk in-depth about someone's particular skill, past projects, or overall behavior.


๐Ÿ“ Explore

The explore page is where all successfully registered members can be found. Once you set up your account, you are automatically added to this explore page. These users are stored in the PlanentScale database.


Excited about this? Join here ๐ŸŽ‰

Tools used in building Spekni

Spekni is completely built with NextJS for the frontend, NodeJS for the backend, and of course, PlanetScale was used for the database. Here's a short list of some of these tools:


UI framework

We used NextJS for the frontend because of its speed and SEO analytics. This is my first time working with NextJS and I have to say, I'm quite impressed with the framework ecosystem. One of the key reasons we used NextJS was to harness its SEO power and automatic routing system, which helped us tremendously to make Spekni fast and have a great SEO.

Without the need of using a routing system like react-router. Here's the folder structure of each of the pages on Spekni. Creating a file on this page automatically creates a route with the subdomain. Which is mind-blowing ๐Ÿคฏ

โ”œโ”€โ”€ pages
โ”‚   โ”œโ”€โ”€ [username]
โ”‚   โ”œโ”€โ”€ 404
โ”‚   โ”œโ”€โ”€ about
โ”‚   โ”œโ”€โ”€ account
โ”‚   โ”œโ”€โ”€ API
โ”‚   โ”‚   โ”œโ”€โ”€ auth
โ”‚   โ”‚   โ”œโ”€โ”€ users
โ”‚   โ”œโ”€โ”€ explore
โ”‚   โ”œโ”€โ”€ login
โ”‚   โ”œโ”€โ”€ _app.js
โ”‚   โ”œโ”€โ”€ _document.js
โ”‚   โ”œโ”€โ”€ error.js
โ”‚   โ”œโ”€โ”€ index.js
โ”‚   โ”œโ”€โ”€ profile.js

TailwindCSS & NextJS module CSS

For the styling, we used TailwindCSS and their UI components from Headless UI to speed up the development of this project. In some cases where Tailwind was not reachable, we complimented with NextJS built-in CSS Modules which allowed us to use the same CSS class name in different files without worrying about collisions. The result of pairing these two resulted in smaller bundle size.



The backend for Spekni was built with NodeJS

Role of PlanetScale

Plantescale is an excellent service for creating databases swiftly. We used PlanetScale to store user data of people signed up on the platform. Here's a graphical representation of what the database schema looks like


We used Prisma as an ORM(Object-relational mapping tool) for our database, we also leveraged Prisma's schema feature to define types for database tables and entities which we pushed to planetscale

And here's an in-depth look of our schema

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["referentialIntegrity"]

datasource db {
  provider             = "mysql"
  url                  = env("DATABASE_URL")
  referentialIntegrity = "prisma"

model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String? @db.Text
  access_token      String? @db.Text
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String? @db.Text
  session_state     String?
  user              User    @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)

model Profile {
  id            Int      @id @default(autoincrement())
  bio           String?  @db.TinyText
  job_title     String?
  githubLink    String?
  username      String?  @unique
  portfolioLink String?
  twitterLink   String?
  linkedinLink  String?
  fullName      String?
  userId        String
  email         String   @unique
  createdAt     DateTime @default(now())
  user          User     @relation(fields: [userId], references: [id], onDelete: Cascade)

model Endorsement {
  id         Int    @id @default(autoincrement())
  userId     String
  skillId    Int
  fromUserId String
  endorsers  User   @relation(fields: [fromUserId], references: [id], onDelete: Cascade)
  skill      Skill  @relation(fields: [skillId], references: [id], onDelete: Cascade)

model Recommendation {
  id         Int    @id @default(autoincrement())
  userId     String
  fromUserId String
  summary    String @db.Text
  createdAt  DateTime @default(now())
  user       User   @relation(fields: [fromUserId], references: [id], onDelete: Cascade)

model Skill {
  id           Int           @id @default(autoincrement())
  skillName    String
  userId       String
  user         User          @relation(fields: [userId], references: [id], onDelete: Cascade)
  endorsements Endorsement[]

model User {
  id              String           @id @default(cuid())
  name            String?
  email           String?          @unique
  emailVerified   DateTime?
  image           String?
  accounts        Account[]
  sessions        Session[]
  endorsements    Endorsement[]
  Profile         Profile[]
  Skill           Skill[]
  recommendations Recommendation[]

model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])

Best Practices

  • Prettier and ESLint for linting, indentation, and consistent code styles.
  • NextJS components and CSS variables to enforce DRY code.
  • Asset optimization for mobile users.
  • Using accessible and semantic elements
  • Great lighthouse audit

We followed best practices and rules to ensure Spekni was accessible, easier to read, review and contribute to. Automatically, NextJS provided much of the configuration by default which is why we opted to use it. Even the lighthouse score was pretty amazing.


Running spekni locally.

git clone https://github.com/Evavic44/spekni.git
cd src

npm install
npm run dev

Open http://localhost:3000 with your browser to see the result.


Building Spekni was an amazing experience, but there were some challenges we had that I believe will be beneficial to explain how we solved them.

Prisma windows error

We had a few system compatibility issues when running prisma generate locally, and what was worse is that this error affected only the windows environment, running Prisma on Mac and Linux environments worked flawlessly.

Here is the error:

error - Error: @prisma/client did not initialize yet. Please run "Prisma generate" and try to import it again.
In case this error is unexpected for you, please report it at https://github.com/prisma/prisma/issues
 Attempted to load @next/swc-win32-x64-msvc, but an error occurred: The specified module could not be found.

It took a lot of research and debugging but we finally discovered the issue was Microsoft VC++ redistributable package Prisma depended on was missing on my PC. After downloading and installing both the x32 and x64 bit versions, prisma generate finally worked!

Additional Features and Summary

These are some of the features we weren't able to implement before launching Spekni. Though we are currently working on them so keep an eye out for the next release. ๐Ÿ‘€

  • Endorsements Export: Users can add endorsements to their portfolio website through an iframe.
  • Recruiters: Find a list of endorsed recruiters with a good track record of hiring the best developers.
  • Explore Search: A feature for finding developers registered on the platform.


It will be impossible to complete this project without giving proper credit to the persons that made this a success. After all, that's what Spekni is all about. In no particular order, here are the credits.

  • PlanetScale - For providing an excellent database service
  • Hashnode - For organizing this hackathon. ๐Ÿ’™
  • James Q Quick - For support and answering our dumbest questions on Discord ๐Ÿ˜†
  • Eleftheria Batsou - For her amazing feedback and reviews.

Creators of Spekni

This project was built for the PlanetScale and Hashnode Hackathon