{"id":25469733,"url":"https://github.com/letstayfoolish/express-api-server-ts","last_synced_at":"2026-04-29T23:31:48.571Z","repository":{"id":220301179,"uuid":"751260094","full_name":"letStayFoolish/express-api-server-ts","owner":"letStayFoolish","description":"Minimal API server based on node.js, express and typescript with user functionality","archived":false,"fork":false,"pushed_at":"2024-03-06T12:15:29.000Z","size":75,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-17T14:41:56.648Z","etag":null,"topics":["express","nodejs","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/letStayFoolish.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-01T08:53:36.000Z","updated_at":"2024-02-25T11:49:16.000Z","dependencies_parsed_at":"2025-02-18T08:32:21.468Z","dependency_job_id":"4e6aab67-c592-4c21-bd7f-d45badf31870","html_url":"https://github.com/letStayFoolish/express-api-server-ts","commit_stats":null,"previous_names":["letstayfoolish/express-api-server-ts"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/letStayFoolish/express-api-server-ts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letStayFoolish%2Fexpress-api-server-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letStayFoolish%2Fexpress-api-server-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letStayFoolish%2Fexpress-api-server-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letStayFoolish%2Fexpress-api-server-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/letStayFoolish","download_url":"https://codeload.github.com/letStayFoolish/express-api-server-ts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letStayFoolish%2Fexpress-api-server-ts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32448387,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T22:27:22.272Z","status":"ssl_error","status_checked_at":"2026-04-29T22:10:49.234Z","response_time":110,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["express","nodejs","typescript"],"created_at":"2025-02-18T08:30:33.170Z","updated_at":"2026-04-29T23:31:48.545Z","avatar_url":"https://github.com/letStayFoolish.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Implement JWT Authentication using Node, Express, TypeScript\r\n\r\n_Sources:_\r\n[https://dev.to/cristain/how-to-implement-jwt-authentication-using-node-express-typescript-app-2023-2c5o](https://dev.to/cristain/how-to-implement-jwt-authentication-using-node-express-typescript-app-2023-2c5o)\r\n[https://blog.stackademic.com/create-a-backend-application-using-express-typescript-and-handle-authentication-1f67be81da60](https://blog.stackademic.com/create-a-backend-application-using-express-typescript-and-handle-authentication-1f67be81da60)\r\n\r\n## 1. Initialize Project\r\n\r\n### 1.1 Before any actions, first things first, if you are going to use code that is already written\r\n\r\n- Create your database using MongoDB. Get connection url which will later be used in `.env` file as `DATABASE_URL`\r\n- In this file as a server path use: `http://localhost:5000` --\u003e `PORT` is 5000 by default, if you want to change it, go to the `.env` file and set constant for PORT as you wish\r\n- Also, do not forget to generate new secret key and put it in `.env` file as `JWT_SECRET`\r\n\r\n\u003e npm init --yes\r\n\r\n## 2. Install dependencies and devDependencies\r\n\r\n- Install dependencies:\r\n\r\n  \u003e npm install express mongoose cors jsonwebtoken dotenv\r\n- and:\r\n  \u003e npm i cookie-parser\r\n\r\n- Install devDependencies:\r\n\r\n  \u003e npm install typescript @types/node @types/express ts-node nodemon @types/cors @types/jsonwebtoken --save-dev\r\n\r\n- Let’s install jsonwebtoken library to work with JWT tokens, and bcryptjs to hash the password:\r\n  \u003e npm i bcryptjs jsonwebtoken\r\n- \u0026 \r\n  \u003e npm i -D @types/bcryptjs @types/jsonwebtoken\r\n- \u0026\r\n  \u003e npm i -D @types/cookie-parser\r\n\r\n- Run below command to create a tsconfig.json file. Or add a `tsconfig.json` file for **typescript** configuration:\r\n  \u003enpx tsc --init\r\n- or\r\n  \u003e tsc --init\r\n\r\n- add this config in `tsconfig.json`:\r\n\r\n```\r\n{\r\n    \"compilerOptions\": {\r\n        /* Language and Environment */\r\n        \"target\": \"es2016\" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,\r\n        /* Modules */\r\n        \"module\": \"commonjs\" /* Specify what module code is generated. */,\r\n        \"rootDir\": \"./\",\r\n        \"outDir\": \"./dist\",\r\n        \"esModuleInterop\": true,\r\n        \"forceConsistentCasingInFileNames\": true /* Ensure that casing is correct in imports. */,\r\n\r\n        /* Type Checking */\r\n        \"strict\": true,\r\n        \"skipLibCheck\": true /* Skip type checking all .d.ts files. */\r\n    }\r\n}\r\n\r\n```\r\n\r\n## 3. Setup .env file\r\n\r\n- First create `.env` file in the root of backend project, after that you can start adding constants\r\n- Add `NODE_ENV=development` so after you can easily change from 'development' tp 'production' and vise versa\r\n- Define PORT\r\n  \u003e PORT=5000\r\n- Also do not forget to define MongoDB url:\r\n  \u003e DATABASE_URL=mongodb+srv://\u003cusername\u003e:\u003cpassword\u003e@...\r\n- Add secret key, needed in moments of creation a jwt tokens. Before that in console type next lines of code:\r\n\r\n```\r\nnode\r\nrequire(\"crypto\").randomBytes(35).toString(\"hex\")\r\n```\r\n\r\n- Take the **Output** from the console and add it to the .env as `SECRET_KEY`\r\n\r\n## 4. Set Up a Mongo Database\r\n\r\n- Outside root file create `db.ts` file for defining the **mongoose** connection:\r\n\r\n```\r\nimport Mongoose from \"mongoose\";\r\nconst localDB = process.env.DATABASE_URL || \"...\"\r\n\r\nconst connectDB = async () =\u003e {\r\n    try {\r\n        const cn = await Mongoose.connect(localDB, {\r\n            useNewUrlParser: true,\r\n            useUnifiedTopology: true,\r\n        })\r\n\r\n        console.log(\"🛢️ Connected To Database\");\r\n    } catch (error) {\r\n        console.log(\"⚠️ Error to connect to Database\");\r\n    }\r\n}\r\n```\r\n\r\n## 5. Setup express server with typescript\r\n\r\n- In the root create a root file, name it: `inde.ts`, or `server.ts` or even `app.ts`:\r\n\r\n```\r\nimport express\r\nimport {Application} from \"express\"\r\nimport mongoose\r\nimport cors\r\nimport dotenv\r\n\r\n// Create the express app (wit its type):\r\nconst app: Application = express()\r\n\r\n// Add cors\r\napp.use(cors())\r\n\r\n// Configure env:\r\ndotenv.config()\r\n\r\n// Add parsers:\r\napp.use(express.json())\r\napp.use(express.urlencoded({ extended: true}))\r\n\r\n// Declare PORT:\r\nconst port = process.env.PORT || 5000;\r\n\r\n// Set initial endpoint in testing purpose:\r\napp.get('/', (req, res) =\u003e {\r\n    res.status(200).send(\"Hello from Exress + TypeScript server\")\r\n})\r\n\r\n\r\n\r\n// Listen server:\r\nconst server = app.listen(port, async () =\u003e{\r\n\r\n    console.log(`Server is running on PORT: ${port}`)\r\n\r\n\r\n    // Connect to the MongoDB:\r\n    try {\r\n       const cn = await mongoose.connect(process.env.DATABASE_URL as string)\r\n\r\n       if(cn) console.log(\"Connected to Database\")\r\n\r\n    } else {\r\n        console.log(\"Error to connect Database\")\r\n    }\r\n})\r\n```\r\n\r\n- To test it, do not forget to add scripts in `package.json` file:\r\n\r\n```\r\n\"scripts\": {\r\n    \"dev\": \"nodemon index.ts\",\r\n    ...\r\n}\r\n```\r\n\r\n\u003e npm run dev\r\n\r\n## 6. Create User Schema\r\n\r\n- Create `models` folder with `userSchema` file\r\n- **Schema** is like a blueprint that shows how the database will be contstructed.\r\n- `userSchema.ts` contains **username** (or email), **password**, and **roles** (or isAdmin)\r\n\r\n```\r\nimport mongoose, { Document, Schema } from \"mongoose\";\r\nimport bcrypt from \"bcryptjs\";\r\n\r\nexport interface IUser extends Document {\r\n  name: string;\r\n  email: string;\r\n  password: string;\r\n  comparePassword: (enteredPassword: string) =\u003e boolean;\r\n  ...reset...\r\n};\r\n\r\nconst userSchema = new Mongoose.Schema({\r\n    name: {type, required, minlength, maxlength}\r\n    email: {type, required, unique, validate, match},\r\n    password: {type, required, minlength: 6, maxlength},\r\n    description: {type, required},\r\n    avatar: {type, required, validation},\r\n    isAdmin: {type, required, default: false}\r\n    or\r\n    role: {type, default: \"basic\", required}\r\n}, {\r\ntimestamps: true}\r\n);\r\n\r\nuserSchema.pre(\"save\", async function (next) {\r\n  if (!this.isModified(\"password\")) {\r\n    next();\r\n  }\r\n\r\n  const salt = await bcrypt.genSalt(10);\r\n  this.password = await bcrypt.hash(this.password, salt);\r\n});\r\n\r\nuserSchema.methods.comparePassword = async function (enteredPassword: string) {\r\n  return await bcrypt.compare(enteredPassword, this.password);\r\n};\r\n\r\nexport const User = mongoose.model(\"User\", userSchema)\r\n```\r\n\r\n- Also good practice is to add email, password validation. If there is more fields to be validate - do it!\r\n\r\n- After creating the user model we are ready to implement the jwt Authentication\r\n\r\n### 6.1 Define function to generate tokens\r\n\r\n- You can create a global helper function `generateToken.ts` in utils:\r\n\r\n```\r\nimport jwt from \"jsonwebtoken\";\r\nimport { Response } from \"express\";\r\n\r\nconst generateToken = (res: Response, userId: string) =\u003e {\r\n  const jwtSecret = process.env.JWT_SECRET || \"\";\r\n  const token = jwt.sign({ userId }, jwtSecret, {\r\n    expiresIn: \"1h\",\r\n  });\r\n\r\n  res.cookie(\"jwt\", token, {\r\n    httpOnly: true,\r\n    secure: process.env.NODE_ENV !== \"development\",\r\n    sameSite: \"strict\",\r\n    maxAge: 60 * 60 * 1000,\r\n  });\r\n};\r\n\r\nconst clearToken = (res: Response) =\u003e {\r\n  res.cookie(\"jwt\", \"\", {\r\n    httpOnly: true,\r\n    expires: new Date(0),\r\n  });\r\n};\r\n\r\nexport { generateToken, clearToken };\r\n```\r\nWe include the userId in the token and set the expiration to 1 hour. Then we set a cookie as jwt with the generated token value. This will set the cookie in the client and for all the subsequent requests, the cookie will be sent automatically in the header.\r\n\r\n## 7. Create User Api to Register\r\n\r\n- Functionality: **adding users**, **getting all users**, **update user info**, **deleting users**, etc...\r\n- You can create new file `auth.ts` or insdide `userController` add `registerUser` method:\r\n\r\n```\r\nconst registerUser = async (req, res, next) =\u003e {\r\n    try {\r\n        // ** Get the User data from body\r\n        const { name, email/username, password, avata, description, isAdmin/role } = req.body\r\n        or take whole user like user = req.body; and later destructure the information from user.\r\n\r\n        // ** Check the email/username if the user already exist in database or not\r\n        // ** Import the user model from \"./models/userModel.ts\"\r\n\r\n        const userExist = await User.findOne({ email/username })\r\n\r\n        if(userExist) {\r\n            res.status(400).json({\r\n                status: 400,\r\n                message: \"The user already exists\",\r\n            })\r\n            return;\r\n            // or:\r\n            throw new Error(\"User already exist\") or \"Email all ready in use\"\r\n        }\r\n\r\n        // If there is no such a user in the data base, create new user:\r\n\r\n        const newUser = await User.create({ name, email/username, password })\r\n\r\n        // ** generate token\r\n\r\n        // ** Do not save the password as a plain text in db!!!\r\n        // use bcrypt to hash password\r\n        // In this case, it was managed within the userModel, hasing the passwords before storing it in db\r\n\r\n        // Send the newUser as response:\r\n        res.status(201).json({\r\n            _id:newUser._id,\r\n            name:newUser.name,\r\n            email/username: newUser.email/newUser.username,\r\n            avatar: newUser.avatar,\r\n            description: newUser.description,\r\n            isAdmin: newUser.isAdmin,\r\n            status: 201,\r\n            message: \"User created successfully\",\r\n        })\r\n    } catch (error) {\r\n        console.log(error)\r\n        res.status(400).json({\r\n            status: 400,\r\n            message: error.message.toString(),\r\n        })\r\n    }\r\n}\r\n```\r\n\r\n## 8. Create Login Api and Implement JWT authentication\r\n\r\n- POST request\r\n- Inside request of a body, we should put: email, passowrd\r\n- `authenticateUser` async function with try/catch block\r\n- Inside try block should be included logic to find user like so `User.findOne({email})` but before that destructure user auth info: **email**, **password**, and check if there are **email** and **passowr**\r\n- When user has been founded in db, check if passwords are matching (function should be inside `models.methods.matchPasswords`)\r\n- If there is user with such a email/username and passwords are matched, we can generateToken.\r\n- Send status(201).json({ status, message, success, user}) to the client\r\n- If not send status(400).json({ status, message, error })\r\n\r\n## 9. Authenticate Users with JSON Web Token (JWT)\r\n\r\n- JSON Web Token help shield a route from an unauthenticated user.\r\n- JWT creates a token, sends it to the client, and then the client uses the token for making requests. It also helps to verify that you are a valid user making those requests.\r\n\r\n```\r\nimport { Request, Response, NextFunction } from \"express\";\r\nimport jwt, { JwtPayload } from \"jsonwebtoken\";\r\nimport User from \"../models/User\";\r\nimport asyncHandler from \"express-async-handler\";\r\nimport { AuthenticationError } from \"./errorMiddleware\";\r\n\r\nconst authenticate = asyncHandler(\r\n  async (req: Request, res: Response, next: NextFunction) =\u003e {\r\n    try {\r\n      let token = req.cookies.jwt;\r\n\r\n      if (!token) {\r\n        throw new AuthenticationError(\"Token not found\");\r\n      }\r\n\r\n      const jwtSecret = process.env.JWT_SECRET || \"\";\r\n      const decoded = jwt.verify(token, jwtSecret) as JwtPayload;\r\n\r\n      if (!decoded || !decoded.userId) {\r\n        throw new AuthenticationError(\"UserId not found\");\r\n      }\r\n\r\n      const user = await User.findById(decoded.userId, \"_id name email\");\r\n\r\n      if (!user) {\r\n        throw new AuthenticationError(\"User not found\");\r\n      }\r\n\r\n      req.user = user;\r\n      next();\r\n    } catch (e) {\r\n      throw new AuthenticationError(\"Invalid token\");\r\n    }\r\n  }\r\n);\r\n\r\nexport { authenticate };\r\n```\r\n- Let’s create a errorMiddleware.ts file and include below code:\r\n```\r\nimport { NextFunction, Request, Response } from \"express\";\r\n\r\nconst errorHandler = (\r\n  err: Error,\r\n  req: Request,\r\n  res: Response,\r\n  next: NextFunction\r\n) =\u003e {\r\n  console.error(err.stack);\r\n\r\n  if (err instanceof AuthenticationError) {\r\n    res.status(401).json({ message: \"Unauthorized: \" + err.message });\r\n  } else {\r\n    res.status(500).json({ message: \"Internal Server Error\" });\r\n  }\r\n};\r\n\r\nclass AuthenticationError extends Error {\r\n  constructor(message: string) {\r\n    super(message);\r\n    this.name = \"AuthenticationError\";\r\n  }\r\n}\r\n\r\nexport { errorHandler, AuthenticationError };\r\n```\r\n- In root file add it and invoke like:\r\n```\r\nimport { errorHandler } from \"./middleware/errorMiddleware\";\r\n\r\napp.use(errorHandler);\r\n```\r\n\r\n### 9.1 Protet the routes\r\n\r\n- To prevent unauthenticated users from accessing the private route, take the token from the cookie, verify the token, and redirect users based on role\r\n- You'll get the token from the client using a node package called cookie-parser.\r\n- Install it, iy you haven't already:\r\n\r\n  \u003e npm i cookie-parser\r\n  \u003e app.use(cookieParser())\r\n\r\n- Create middleware that verifies the token and grants access to your private route\r\n- Admin Authentication:\r\n\r\n```\r\nadminAuth = (req, res, next) =\u003e {\r\n    // grab token from cookie\r\n    token = req.cookies.jwt\r\n\r\n    // Check if there is token inside cookies:\r\n    if (token) {\r\n        jwt.verify(token, secret, (err, decodedToken) =\u003e {\r\n            if (err) {\r\n                return res.status(401).json({\r\n                    status: 401,\r\n                    message: \"Not authorized\",\r\n                    error: err\r\n                })\r\n            } else {                                 // means isAdmin: false\r\n                if (decodedToken.role !== \"admin\" || !decodedToken.isAdmin ) {\r\n                    return res.status(401).json({ message: \"Not authorized\" })\r\n                } else {\r\n                    next()\r\n                }\r\n            }\r\n        })\r\n    } else {\r\n        return res.status(401).json({\r\n            status: 401,\r\n            message: \"Not authorized, token not available\",\r\n        })\r\n    }\r\n}\r\n```\r\n\r\n- Protect Routes - User Authentication\r\n\r\n```\r\nprotect = (req, res, next) =\u003e {\r\n    // grab token from cookie\r\n    token = req.cookies.jwt\r\n\r\n    // Check if there is token inside cookies:\r\n    if (token) {\r\n        jwt.verify(token, secret, (err, decodedToken) =\u003e {\r\n            if (err) {\r\n                return res.status(401).json({\r\n                    status: 401,\r\n                    message: \"Not authorized\",\r\n                    error: err\r\n                })\r\n            } else {\r\n               next()\r\n            }\r\n        })\r\n    } else {\r\n        return res.status(401).json({\r\n            status: 401,\r\n            message: \"Not authorized, token not available\",\r\n        })\r\n    }\r\n}\r\n```\r\n\r\n- Now protect the routes by adding middleware\r\n  \u003e router.route('/').get(adminAuth, getAllUsersController)\r\n- And so on for all of the routes that need to be protected\r\n\r\n### 9.2 Install helmet package also and add it to the index.ts file. This secures the app by adding a set of response headers.\r\n\u003e npm i helmet\r\n- The index.ts up to now should be as below (* this should match your code only in case that you did everything like in the [source](https://blog.stackademic.com/create-a-backend-application-using-express-typescript-and-handle-authentication-1f67be81da60)):\r\n```\r\nimport express from \"express\";\r\nimport authRouter from \"./routes/authRouter\";\r\nimport connectUserDB from \"./connections/userDB\";\r\nimport dotenv from \"dotenv\";\r\nimport cors from \"cors\";\r\nimport helmet from \"helmet\";\r\nimport bodyParser from \"body-parser\";\r\nimport cookieParser from \"cookie-parser\";\r\nimport userRouter from \"./routes/userRouter\";\r\nimport { authenticate } from \"./middleware/authMiddleware\";\r\nimport { errorHandler } from \"./middleware/errorMiddleware\";\r\n\r\ndotenv.config();\r\n\r\ninterface UserBasicInfo {\r\n  _id: string;\r\n  name: string;\r\n  email: string;\r\n}\r\n\r\ndeclare global {\r\n  namespace Express {\r\n    interface Request {\r\n      user?: UserBasicInfo | null;\r\n    }\r\n  }\r\n}\r\n\r\nconst app = express();\r\nconst port = process.env.PORT || 8000;\r\napp.use(helmet());\r\n\r\napp.use(\r\n  cors({\r\n    origin: \"http://localhost:3000\",\r\n    credentials: true,\r\n  })\r\n);\r\n\r\napp.use(cookieParser());\r\n\r\napp.use(bodyParser.json()); // To recognize the req obj as a json obj\r\napp.use(bodyParser.urlencoded({ extended: true })); // To recognize the req obj as strings or arrays. extended true to handle nested objects also\r\n\r\napp.listen(port, () =\u003e {\r\n  console.log(`Server is running on port ${port}`);\r\n});\r\n\r\napp.use(authRouter);\r\napp.use(\"/users\", authenticate, userRouter);\r\n\r\napp.use(errorHandler);\r\n\r\nconnectUserDB(); // if you have making DB connection inside a different file;\r\n```\r\n## 10. Logout\r\n\r\n- set cookie to \"\" like so:\r\n  \u003e res.cookie(cookieName, \"\", { httpOnly: true, expires: new Date(0)})\r\n- It has to be post request, protected route.\r\n\r\n## 11. Get User Profile\r\n\r\n- grab **id** from request (user or body)\r\n- Define user using `User.findById(id)`\r\n- Check if there is such a user, and send status(201), send json({id, name, email, isAdmin}) if not, send status(400) with \"Invalid user data\" message\r\n-\r\n\r\n## 12. Update User Information\r\n\r\n- Create controller `getAllUsers` by using `User.find({})` empty object to call everything\r\n- check if there is userList, and send status(200) and json({ status, userList, message})\r\n- If !userList set status(400) json({ status, message})\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fletstayfoolish%2Fexpress-api-server-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fletstayfoolish%2Fexpress-api-server-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fletstayfoolish%2Fexpress-api-server-ts/lists"}