/kodo-backend

Primary LanguageTypeScriptMIT LicenseMIT

Exercise:

Implement search and sort of a feed, with pagination.

Search:

  • should search in fields name and description
  • By default (i.e. for no search term) all posts should match the query.
  • Support exact match when the query contains a phrase within double quotes (like Google does)
  • Examples: Given the following posts:
    • Post 1 with name: "The Lord of the Rings: The Return of the King".
    • Post 2 with name: "The Lion King".
    Search Term Post 1 matches? Post 2 matches?
    the king Yes Yes
    "the king" Yes No

Sort:

  • allow sorting by name and dateLastEdited

Pagination:

  • Use 'page numbers' style of pagination
  • Include total count in the response matching the current query result

Backend:

  • Use a strongly typed language like Typescript (Node.js) or any JVM based language, with or without an application framework.
  • The web APIs can be ReSTful or GraphQL-based.
  • Search should be implemented in-memory. Design it in such a way that it would be easy to replace it with a microservice in future.
  • Use the attached mock_data.json as seed data
  • Write meaningful unit and/or integration tests where appropriate, preferably following TDD.

Prerequisites

To build and run this app locally you will need a few things:

Getting started

  • Clone the repository
git clone --depth=1 https://github.com/polcham/mongoose-express-ts.git <project_name>
  • Install dependencies
cd <project_name>
npm install
npm run tsc
  • Build and run the project with auto reload (nodemon)
npm run server
  • Build and run the project
npm run start

Finally, navigate to http://localhost:5000/ and you should see the API running!

Project Structure

The most obvious difference in a TypeScript + Node project is the folder structure. In a TypeScript project, it's best to have separate source and distributable files. TypeScript (.ts) files live in your src folder and after compilation are output as JavaScript (.js) in the same folder.

The full folder structure of this app is explained below:

Note! Make sure you have already built the app using npm run start

Name Description
config Contains config environment to be used by the config package, such as MongoDB URI, jwtSecret, and etc.
node_modules Contains all your npm dependencies
REST Contains all API requests to test the routes, used with REST Client VSCode extension
src Contains your source code that will be compiled
src/middleware Contains the middlewares to intercept requests
src/models Models define Mongoose schemas that will be used in storing and retrieving data from MongoDB
src/routes Routes define the endpoints of your API
src/types Contains all your custom types to better handle type checking with TypeScript
src/server.ts Entry point to your express app
package.json File that contains npm dependencies as well as build scripts
tsconfig.json Config settings for compiling server code written in TypeScript
tslint.json Config settings for TSLint code style checking

Configuring TypeScript compilation

TypeScript uses the file tsconfig.json to adjust project compile options. Let's dissect this project's tsconfig.json, starting with the compilerOptions which details how your project is compiled.

    "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true,
    "target": "es6",
    "noImplicitAny": true,
    "moduleResolution": "node",
    "sourceMap": true,
    "baseUrl": ".",
    "paths": {
      "*": ["node_modules/*", "src/types/*"]
    }
  }
compilerOptions Description
"module": "commonjs" The output module type (in your .js files). Node uses commonjs, so that is what we use
"esModuleInterop": true, Allows usage of an alternate module import syntax: import foo from 'foo';
"target": "es6" The output language level. Node supports ES6, so we can target that here
"noImplicitAny": true Enables a stricter setting which throws errors when something has a default any value
"moduleResolution": "node" TypeScript attempts to mimic Node's module resolution strategy. Read more here
"sourceMap": true We want source maps to be output along side our JavaScript. See the debugging section
"baseUrl": "." Part of configuring module resolution. See path mapping section
paths: {...} Part of configuring module resolution. See path mapping section

The rest of the file define the TypeScript project context. The project context is basically a set of options that determine which files are compiled when the compiler is invoked with a specific tsconfig.json. In this case, we use the following to define our project context:

    "include": [
        "src/**/*"
    ]

include takes an array of glob patterns of files to include in the compilation. This project is fairly simple and all of our .ts files are under the src folder.

Running the build

All the different build steps are orchestrated via npm scripts. Npm scripts basically allow us to call (and chain) terminal commands via npm. This is nice because most JavaScript tools have easy to use command line utilities allowing us to not need grunt or gulp to manage our builds. If you open package.json, you will see a scripts section with all the different scripts you can call. To call a script, simply run npm run <script-name> from the command line. You'll notice that npm scripts can call each other which makes it easy to compose complex builds out of simple individual build scripts. Below is a list of all the scripts this template has available:

Npm Script Description
tsc Transpiles TypeScript codes to JavaScript.
watch-tsc Transpiles TypeScript codes to JavaScript, with auto reload.
deploy Runs node on src/server.js which is the app's entry point.
watch-deploy Runs node on src/server.js which is the app's entry point, with auto reload.
server Transpiles TypeScript codes to JavaScript then run node on src/server.js with auto reload.
start Transpiles TypeScript codes to JavaScript then run node on src/server.js.

Since we're developing with TypeScript, it is important for the codes to be transpiled first to JavaScript before running the node server. It is best to deploy the app using: npm run server or npm run start command.

VSCode Extensions

To enhance your development experience while working in VSCode, I provided you with a list of suggested extensions while working on this project:

Dependencies

Dependencies are managed through package.json. In that file you'll find two sections:

dependencies

Package Description
bcryptjs Library for hashing and salting user passwords.
config Universal configurations for your app.
express Node.js web framework.
express-validator Easy form validation for Express.
gravatar Generate Gravatar URLs based on gravatar specs.
http-status-codes HTTP status codes constants.
jsonwebtoken JsonWebToken implementation for Node.js.
mongoose MongoDB modeling tool in an async environment.
request Simplified HTTP client for Node.js.
typescript Typed superset of JavaScript.

devDependencies

Since TypeScript is used, dependencies should be accompanied with their corresponding DefinitelyTyped @types package.

Package Description
@types/bcryptjs DefinitelyTyped for bcryptjs
@types/config DefinitelyTyped for config
@types/express DefinitelyTyped for express
@types/gravatar DefinitelyTyped for gravatar
@types/jsonwebtoken DefinitelyTyped for jsonwebtoken
@types/mongoose DefinitelyTyped for mongoose
concurrently Run multiple commands concurrently
nodemon Reload node application on code changes

To install or update these dependencies you can use npm install or npm update.