Imagine you have cool frontend app that is connected with your awesomome backend application that serves an API. The API is obviously expecting some input data in some of the requests, and sometimes it returns some data in response. I say some, because your web app is not aware what kind of data is supposed to send and what should expect in return. So today I will show you how to create contract package with Typescript and deploy it in Github Packages.
NOTE: We won't be covering here how to create backend nor frontend application, so the contract package will be in seperate repository. You could create monorepo with api and contracts.
You will need:
Outline
Let's say we have a website in which the user must be logged in order to use it. Frontend wants to fetch basic info about the user. The API is providing such endpoint and it returns an object of type UserOutput
interface UserOutput {
username: string;
email: string;
}
What should we do so that web app has access to given type?
Creating new project
Let's go straight to the point.
First create a directory for new project
mkdir my-project-contracts
cd my-project-contracts
Initialize new npm project with git and typescript (for now skip all the settings in package.json
)
git init
npm init
npm i typescript --save-dev
npx tsc --init
Create UserOutput.ts
file and add inside your first contract
export default interface UserOutput {
username: string;
email: string;
}
Create index.ts
file and import your contract
import UserOutput from "./UserOutput";
export { UserOutput };
Now we tweak tsconfig.json
. For contracts it's sufficient to emit only declaration files. We specify output directory to dist
folder. So our config file should look like this
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Emit */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"declarationMap": true, /* Create sourcemaps for d.ts files. */
"emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
"outDir": "dist", /* Specify an output folder for all emitted files. */
}
}
Let's not forget about .gitignore
in order to not publish unnecessary folders
/dist
/node_modules
So far so good!
Now it's time to prepare our project to enable it for publishing as a package in Github Package registry with usage of Github Actions.
Before we go forward we need to set up personal access token (classic). To do that follow the steps here.
Make sure you select the scopes which define the access for personal tokens.
IMPORTANT: Keep your token for later! We're gonna use it ;)
Ok, when it's done, we come back to our project once again to finish the setup.
Change your package.json
so that:
-
name
follows@YOUR-USERNAME/YOUR-REPOSITORY
- set the test script to
exit 0
- set build command to build
*.d.ts
files - tell npm which scope and registry to publish packages by specifying the
publishConfig
key - specify version of the package
- use
files
key to indicate with files should be included when publishing (read more about here) in my case it looks like this:
{
"name": "@gh-username/my-project-contracts",
"version": "1.0.0",
"description": "Example of creating package with contracts usin Github Packages",
"types": "./dist/index.d.ts",
"publishConfig": {
"gh-username:registry": "https://npm.pkg.github.com"
},
"files": [
"dist"
],
"scripts": {
"test": "exit 0",
"build": "npx tsc"
},
"author": "me",
"devDependencies": {
"typescript": "^5.1.3"
}
}
And the last thing to do in our project is to set up pipeline for building and publishing it. So, following Quickstart example:
create a
.github/workflows
directory. In that directory, create a file namedrelease-package.yml
.copy the following YAML content into the
release-package.yml
file.
name: My package with contracts
on:
release:
types: [created]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npm run build
- run: npm test
publish-gpr:
needs: build
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
registry-url: https://npm.pkg.github.com/
- run: npm ci
- run: npm run build
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
As you can see, the aceess token we set up earlier, is used to check up permissions for package registries.
Aaaand looks like we've done! Well, almost, now you have to create a repository in Github and push your changes.
Create release
If you finished creating new repo for contracts, you'd be ready to create a release and build your new package.
- On GitHub.com, navigate to the main page of the repository.
- To the right of the list of files, click Releases.
- At the top of the page, click Draft a new release.
- Choose a tag and a title for the release.
- Click Publish release.
- Go to the Actions tab and see you release action
After it finishes, in the main page of repository you should see your package
NOTE: For more detailed description about creating releases, check official documentation.
Final touch: Use contract package in your project
We almost there! In the project you want to use the contract package, set .npmrc
file with information about github registry
//npm.pkg.github.com/:_authToken={GITHUB_TOKEN}
@gh-username:registry=https://npm.pkg.github.com
NOTE: Create new GITHUB_TOKEN
only with read:packages
scope.
If you use npm
, install your packages with npm i @YOUR-USERNAME/YOUR-REPOSITORY --save-dev
.
If you use yarn
you will have to point with url to your repository, check conversation on Stack Overflow.
And that's it! Thank you for taking your time :)
Top comments (0)