NC

Naijacloud

Docs

DashboardStart free

API

/

Projects, services and deploys

Projects, services and deploys

The provisioning path in four calls — project, environment, service, deploy. Everything here is GraphQL at `https://api.naijacloud.com/graphql`; the first three need `PLATFORM_API` and the last needs `DEPLOYS`.

6 min read

The shape

A project holds environments, and an environment holds services. A service is the unit that runs: a web app, a static site, a cron job, or a managed database. See core concepts for how they relate.

Every call below takes a teamId. Get it once from getMyTeams and keep it.

1. Create a project

createProject takes plain arguments rather than an input object.

ArgumentTypeRequiredNotes
teamIdID!yesThe workspace that will own it
nameString!yesSlug-shaped, unique in the workspace
displayNameStringnoWhat the dashboard shows
descriptionStringno

Create a project

curl https://api.naijacloud.com/graphql \ -H "Authorization: Bearer $NC_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "mutation($teamId: ID!, $name: String!) { createProject(teamId: $teamId, name: $name) { id name environments { id name } } }", "variables": { "teamId": "3f2b…", "name": "storefront" } }'

Response

{ "data": { "createProject": { "id": "9c11…", "name": "storefront", "environments": [{ "id": "4ab0…", "name": "prod" }] } } }
i

A new project arrives with an environment already in it. Read environments off the response rather than creating one — you need its id for the next call, and a second prod will be refused as a duplicate.

2. Add an environment (optional)

Only when you want more than the one you were given — a staging beside prod.

createEnvironment

mutation($projectId: ID!, $name: String!) { createEnvironment(projectId: $projectId, name: $name) { id name } }

3. Create a service

createService takes a single CreateServiceInput. It covers every service type, so most fields are optional and which ones matter depends on type and sourceType.

Always required:

FieldTypeNotes
environmentIdID!From step 1 or 2
nameString!Display name; the URL slug is derived from it
typeServiceType!WEB, STATIC, CRON, POSTGRES, MYSQL, MARIADB, MONGODB, REDIS, VALKEY

For anything that runs your code, pick where it comes from:

sourceTypeThen set
GITHUB_APPrepoFullName, branch, installationId
PUBLIC_GITgitUrl, branch
DOCKER_IMAGEimage, optionally imageCommand

And the common tuning fields:

FieldTypeNotes
regionStringaf-west (Port Harcourt) or eu-west (UK, France). Defaults to the workspace's default region
tierServiceTierFREE, STARTER, STANDARD, PRO. Sizes and prices are in the pricing catalogue
portIntThe port your app listens on. Set it — a wrong one is a 502 nothing else will explain
buildCommand, startCommandStringOverride what the builder detected
rootDir, monorepoStrategyFor a service inside a monorepo
envVars[EnvVarInput!]{ key, value, secret?, scope? }, seeded at creation
scheduleStringCron expression. CRON services only
staticOutputDir, staticSpaSTATIC services only

A web service from a public repo

curl https://api.naijacloud.com/graphql \ -H "Authorization: Bearer $NC_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "mutation($input: CreateServiceInput!) { createService(CreateServiceInput: $input) { id name type status url } }", "variables": { "input": { "environmentId": "4ab0…", "name": "Storefront API", "type": "WEB", "sourceType": "PUBLIC_GIT", "gitUrl": "https://github.com/acme/storefront.git", "branch": "main", "region": "af-west", "tier": "STARTER", "port": 3000, "envVars": [{ "key": "NODE_ENV", "value": "production" }] } } }'

A managed database is the same call with a data type and no source:

variables for a Postgres instance

{ "input": { "environmentId": "4ab0…", "name": "Storefront DB", "type": "POSTGRES", "region": "af-west", "tier": "STARTER" } }

Creating a service starts its first build. status walks PROVISIONING → ACTIVE, and url fills in once it is routable.

4. Deploy

triggerDeploy

mutation($serviceId: ID!) { triggerDeploy(serviceId: $serviceId) { id status commitSha } }
!

This one needs the DEPLOYS scope, not PLATFORM_API. Creating a service and deploying it are different areas on purpose: a CI key that only ships existing services should not also be able to create and delete them. A key that does both needs both ticked.

A deployment moves through QUEUED → BUILDING → DEPLOYING → RUNNING, or FAILED. A poller will also see CANCELLED and SUPERSEDED — the second means a newer deploy overtook this one, which is a normal outcome of pushing twice, not a failure. Poll it, or read the log:

Watching a deploy

query($serviceId: ID!) { getDeployments(serviceId: $serviceId, OffsetPaginationArgs: { limit: 1 }) { items { id status createdAt finishedAt } } }

Build and runtime logs come from getDeploymentLogs(deploymentId, OffsetPaginationArgs) — note it keys on the deployment, not the service — and is also DEPLOYS.

Reading back

OperationReturns
getProjects(teamId, OffsetPaginationArgs)A page of projects
getMyServices(OffsetPaginationArgs)Every service across every workspace you belong to
getService(id)One service with its deployments, domains and connection

Every list is paginated the same way. Pass OffsetPaginationArgs: { page, limit, search, sortBy, sortOrder }page is 1-based, limit defaults to 25 and caps at 100 — and read pageInfo back: { page, limit, totalCount, totalPages, hasNextPage, hasPreviousPage }. Nothing returns an unbounded list, so a script that walks every service has to follow hasNextPage.