Express frameworks docs + CLI (#512)
* Manual setup docs * Added the onboarding * The emails package now works with Node > 18 * Added CLi support for Express, including custom init command finished messages * Need to actually log out the installation complete message… * Fix for an old Remix reference * Renamed the page export * Use resolvedOptions.triggerUrl * Typo in manual instructions
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
Added Express support to the CLI
|
||||
@@ -51,7 +51,7 @@ export function FrameworkSelector() {
|
||||
<FrameworkLink to={projectSetupNextjsPath(organization, project)} supported>
|
||||
<NextjsLogo className="w-32" />
|
||||
</FrameworkLink>
|
||||
<FrameworkLink to={projectSetupExpressPath(organization, project)}>
|
||||
<FrameworkLink to={projectSetupExpressPath(organization, project)} supported>
|
||||
<ExpressLogo className="w-36" />
|
||||
</FrameworkLink>
|
||||
<FrameworkLink to={projectSetupRemixPath(organization, project)} supported>
|
||||
|
||||
+105
-10
@@ -1,21 +1,116 @@
|
||||
import { ExpressLogo } from "~/assets/logos/ExpressLogo";
|
||||
import { FrameworkComingSoon } from "~/components/frameworks/FrameworkComingSoon";
|
||||
import { ChatBubbleLeftRightIcon, Squares2X2Icon } from "@heroicons/react/20/solid";
|
||||
import invariant from "tiny-invariant";
|
||||
import { Feedback } from "~/components/Feedback";
|
||||
import { PageGradient } from "~/components/PageGradient";
|
||||
import { InitCommand, RunDevCommand, TriggerDevStep } from "~/components/SetupCommands";
|
||||
import { StepContentContainer } from "~/components/StepContentContainer";
|
||||
import { InlineCode } from "~/components/code/InlineCode";
|
||||
import { BreadcrumbLink } from "~/components/navigation/NavBar";
|
||||
import { Badge } from "~/components/primitives/Badge";
|
||||
import { Button, LinkButton } from "~/components/primitives/Buttons";
|
||||
import { Callout } from "~/components/primitives/Callout";
|
||||
import { ClipboardField } from "~/components/primitives/ClipboardField";
|
||||
import { Header1 } from "~/components/primitives/Headers";
|
||||
import { Paragraph } from "~/components/primitives/Paragraph";
|
||||
import { StepNumber } from "~/components/primitives/StepNumber";
|
||||
import { useAppOrigin } from "~/hooks/useAppOrigin";
|
||||
import { useDevEnvironment } from "~/hooks/useEnvironments";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
import { useProjectSetupComplete } from "~/hooks/useProjectSetupComplete";
|
||||
import { Handle } from "~/utils/handle";
|
||||
import { trimTrailingSlash } from "~/utils/pathBuilder";
|
||||
import { projectSetupPath, trimTrailingSlash } from "~/utils/pathBuilder";
|
||||
|
||||
export const handle: Handle = {
|
||||
breadcrumb: (match) => <BreadcrumbLink to={trimTrailingSlash(match.pathname)} title="Express" />,
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
useProjectSetupComplete();
|
||||
const devEnvironment = useDevEnvironment();
|
||||
invariant(devEnvironment, "Dev environment must be defined");
|
||||
const appOrigin = useAppOrigin();
|
||||
|
||||
return (
|
||||
<FrameworkComingSoon
|
||||
frameworkName="Express"
|
||||
githubIssueUrl="https://github.com/triggerdotdev/trigger.dev/issues/451"
|
||||
githubIssueNumber={451}
|
||||
>
|
||||
<ExpressLogo className="w-56" />
|
||||
</FrameworkComingSoon>
|
||||
<PageGradient>
|
||||
<div className="mx-auto max-w-3xl">
|
||||
<div className="flex items-center justify-between">
|
||||
<Header1 spacing className="text-bright">
|
||||
Get setup in 5 minutes
|
||||
</Header1>
|
||||
<div className="flex items-center gap-2">
|
||||
<LinkButton
|
||||
to={projectSetupPath(organization, project)}
|
||||
variant="tertiary/small"
|
||||
LeadingIcon={Squares2X2Icon}
|
||||
>
|
||||
Choose a different framework
|
||||
</LinkButton>
|
||||
<Feedback
|
||||
button={
|
||||
<Button variant="tertiary/small" LeadingIcon={ChatBubbleLeftRightIcon}>
|
||||
I'm stuck!
|
||||
</Button>
|
||||
}
|
||||
defaultValue="help"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Callout
|
||||
variant={"info"}
|
||||
to="https://github.com/triggerdotdev/trigger.dev/discussions/430"
|
||||
className="mb-8"
|
||||
>
|
||||
Trigger.dev has full support for serverless. We will be adding support for long-running
|
||||
servers soon.
|
||||
</Callout>
|
||||
<div>
|
||||
<StepNumber
|
||||
stepNumber="1"
|
||||
title="Manually set up Trigger.dev in your existing Express project"
|
||||
/>
|
||||
<StepContentContainer className="flex flex-col gap-2">
|
||||
<Paragraph className="mt-2">Copy your server API Key to your clipboard:</Paragraph>
|
||||
<div className="mb-2 flex w-full items-center justify-between">
|
||||
<ClipboardField
|
||||
secure
|
||||
className="w-fit"
|
||||
value={devEnvironment.apiKey}
|
||||
variant={"secondary/medium"}
|
||||
icon={<Badge variant="outline">Server</Badge>}
|
||||
/>
|
||||
</div>
|
||||
<Paragraph>Now follow this guide:</Paragraph>
|
||||
<LinkButton
|
||||
to="https://trigger.dev/docs/documentation/guides/manual/express"
|
||||
variant="primary/medium"
|
||||
TrailingIcon="external-link"
|
||||
>
|
||||
Manual installation guide
|
||||
</LinkButton>
|
||||
</StepContentContainer>
|
||||
<StepNumber stepNumber="2" title="Run your Express app" />
|
||||
<StepContentContainer>
|
||||
<RunDevCommand />
|
||||
<Callout variant="info">
|
||||
You may be using the `start` script instead, in which case substitute `dev` in the
|
||||
above commands.
|
||||
</Callout>
|
||||
</StepContentContainer>
|
||||
<StepNumber stepNumber="3" title="Run the CLI 'dev' command" />
|
||||
<StepContentContainer>
|
||||
<TriggerDevStep />
|
||||
</StepContentContainer>
|
||||
<StepNumber stepNumber="6" title="Wait for Jobs" displaySpinner />
|
||||
<StepContentContainer>
|
||||
<Paragraph>This page will automatically refresh.</Paragraph>
|
||||
</StepContentContainer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageGradient>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1 +1,193 @@
|
||||
We're in the process of building support for the Express framework. You can follow along with progress or contribute via [this GitHub issue](https://github.com/triggerdotdev/trigger.dev/issues).
|
||||
## Installing Required Packages
|
||||
|
||||
Start by installing the necessary packages in your Express.js project directory. You can use npm, pnpm, or yarn as your package manager.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npm install @trigger.dev/sdk @trigger.dev/express
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm install @trigger.dev/sdk @trigger.dev/express
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn add @trigger.dev/sdk @trigger.dev/express
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<Note>Ensure that you execute this command within a Express project.</Note>
|
||||
|
||||
## Obtaining the Development Server API Key
|
||||
|
||||
To locate your development Server API key, login to the [Trigger.dev
|
||||
dashboard](https://cloud.trigger.dev) and select the Project you want to
|
||||
connect to. Then click on the Environments & API Keys tab in the left menu.
|
||||
You can copy your development Server API Key from the field at the top of this page.
|
||||
(Your development key will start with `tr_dev_`).
|
||||
|
||||
## Adding Environment Variables
|
||||
|
||||
Create a `.env` file at the root of your project and include your Trigger API key and URL like this:
|
||||
|
||||
```bash
|
||||
TRIGGER_API_KEY=ENTER_YOUR_DEVELOPMENT_API_KEY_HERE
|
||||
TRIGGER_API_URL=https://api.trigger.dev # this is only necessary if you are self-hosting
|
||||
```
|
||||
|
||||
Replace `ENTER_YOUR_DEVELOPMENT_API_KEY_HERE` with the actual API key obtained from the previous step.
|
||||
|
||||
## Configuring the Trigger Client
|
||||
|
||||
Create a file for your Trigger client, in this case we create it at `<root>/trigger.(ts/js)`
|
||||
|
||||
```ts trigger.(ts/js)
|
||||
import { TriggerClient } from "@trigger.dev/sdk";
|
||||
|
||||
export const client = new TriggerClient({
|
||||
id: "my-app",
|
||||
apiKey: process.env.TRIGGER_API_KEY!,
|
||||
apiUrl: process.env.TRIGGER_API_URL!,
|
||||
});
|
||||
```
|
||||
|
||||
Replace **"my-app"** with an appropriate identifier for your project.
|
||||
|
||||
## Adding the API endpoint
|
||||
|
||||
There are a few different options depending on how your Express project is configured.
|
||||
|
||||
- App middleware
|
||||
- Entire app for Trigger.dev (only relevant if it's the only thing your project is for)
|
||||
|
||||
Select the appropriate code example from below:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```typescript app middleware
|
||||
//import the client from the other file
|
||||
import { client } from "./trigger";
|
||||
import { createMiddleware } from "@trigger.dev/express";
|
||||
|
||||
//import your job files
|
||||
import "./jobs/example";
|
||||
|
||||
//..your existing Express code
|
||||
const app: Express = express();
|
||||
|
||||
//add the middleware
|
||||
app.use(createMiddleware(client));
|
||||
|
||||
//..the rest of your Express code
|
||||
```
|
||||
|
||||
```typescript entire app
|
||||
//if the entire app is just for Trigger.dev
|
||||
import { client } from "./trigger";
|
||||
import { createExpressServer } from "@trigger.dev/express";
|
||||
|
||||
//import your job files
|
||||
import "./jobs/example";
|
||||
|
||||
//this creates an app
|
||||
createExpressServer(client);
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Creating the Example Job
|
||||
|
||||
Create a Job file. In this case created `<root>/jobs/example.(ts/js)`
|
||||
|
||||
```typescript jobs/example.(ts/js)
|
||||
import { eventTrigger } from "@trigger.dev/sdk";
|
||||
import { client } from "../trigger";
|
||||
|
||||
// your first job
|
||||
client.defineJob({
|
||||
id: "example-job",
|
||||
name: "Example Job",
|
||||
version: "0.0.1",
|
||||
trigger: eventTrigger({
|
||||
name: "example.event",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.logger.info("Hello world!", { payload });
|
||||
|
||||
return {
|
||||
message: "Hello world!",
|
||||
};
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Adding Configuration to `package.json`
|
||||
|
||||
Inside the `package.json` file, add the following configuration under the root object:
|
||||
|
||||
```json
|
||||
"trigger.dev": {
|
||||
"endpointId": "my-app"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Replace **"my-app"** with the appropriate identifier you used in the trigger.js configuration file.
|
||||
|
||||
## Running
|
||||
|
||||
### Run your Express app
|
||||
|
||||
Run your Express app locally, like you normally would. For example:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npm run dev
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn run dev
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<Note>You might use `npm run start` instead of dev</Note>
|
||||
|
||||
### Run the CLI 'dev' command
|
||||
|
||||
In a **_separate terminal window or tab_** run:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npx @trigger.dev/cli@latest dev
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm dlx @trigger.dev/cli@latest dev
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn dlx @trigger.dev/cli@latest dev
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
<br />
|
||||
<Note>
|
||||
You can optionally pass the port if you're not running on 3000 by adding
|
||||
`--port 3001` to the end
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
You can optionally pass the hostname if you're not running on localhost by adding
|
||||
`--hostname <host>`. Example, in case your Express is running on 0.0.0.0: `--hostname 0.0.0.0`.
|
||||
</Note>
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/degit": "^2.8.3",
|
||||
"boxen": "^7.1.1",
|
||||
"chalk": "^5.2.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"commander": "^9.4.1",
|
||||
|
||||
@@ -129,18 +129,20 @@ export const initCommand = async (options: InitCommandOptions) => {
|
||||
|
||||
await addConfigurationToPackageJson(resolvedPath, resolvedOptions);
|
||||
|
||||
await printNextSteps(resolvedOptions, authorizedKey, packageManager, framework);
|
||||
const projectUrl = `${resolvedOptions.triggerUrl}/orgs/${authorizedKey.organization.slug}/projects/${authorizedKey.project.slug}`;
|
||||
if (framework.printInstallationComplete) {
|
||||
await framework.printInstallationComplete(projectUrl);
|
||||
} else {
|
||||
await printNextSteps(projectUrl, packageManager, framework);
|
||||
}
|
||||
telemetryClient.init.completed(resolvedOptions);
|
||||
};
|
||||
|
||||
async function printNextSteps(
|
||||
options: ResolvedOptions,
|
||||
authorizedKey: WhoamiResponse,
|
||||
projectUrl: string,
|
||||
packageManager: PackageManager,
|
||||
framework: Framework
|
||||
) {
|
||||
const projectUrl = `${options.triggerUrl}/orgs/${authorizedKey.organization.slug}/projects/${authorizedKey.project.slug}`;
|
||||
|
||||
logger.success(`✔ Successfully initialized Trigger.dev!`);
|
||||
|
||||
logger.info("Next steps:");
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import mock from "mock-fs";
|
||||
import { Express } from ".";
|
||||
import { getFramework } from "..";
|
||||
import { pathExists } from "../../utils/fileSystem";
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
describe("Express project detection", () => {
|
||||
test("has dependency", async () => {
|
||||
mock({
|
||||
"package.json": JSON.stringify({ dependencies: { express: "1.0.0" } }),
|
||||
});
|
||||
|
||||
const framework = await getFramework("", "npm");
|
||||
expect(framework?.id).toEqual("express");
|
||||
});
|
||||
|
||||
test("no dependency", async () => {
|
||||
mock({
|
||||
"package.json": JSON.stringify({ dependencies: { foo: "1.0.0" } }),
|
||||
});
|
||||
|
||||
const framework = await getFramework("", "npm");
|
||||
expect(framework?.id).not.toEqual("express");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Framework, ProjectInstallOptions } from "..";
|
||||
import { InstallPackage } from "../../utils/addDependencies";
|
||||
import { PackageManager } from "../../utils/getUserPkgManager";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { readPackageJson } from "../../utils/readPackageJson";
|
||||
import { standardWatchFilePaths } from "../watchConfig";
|
||||
import boxen from "boxen";
|
||||
|
||||
export class Express implements Framework {
|
||||
id = "express";
|
||||
name = "Express";
|
||||
|
||||
async isMatch(path: string, packageManager: PackageManager): Promise<boolean> {
|
||||
//check for the express package
|
||||
const packageJsonContent = await readPackageJson(path);
|
||||
if (packageJsonContent?.dependencies?.express) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async dependencies(): Promise<InstallPackage[]> {
|
||||
return [
|
||||
{ name: "@trigger.dev/sdk", tag: "latest" },
|
||||
{ name: "@trigger.dev/express", tag: "latest" },
|
||||
];
|
||||
}
|
||||
|
||||
possibleEnvFilenames(): string[] {
|
||||
return [".env"];
|
||||
}
|
||||
|
||||
async install(path: string, { typescript, endpointSlug }: ProjectInstallOptions): Promise<void> {}
|
||||
|
||||
async postInstall(path: string, options: ProjectInstallOptions): Promise<void> {}
|
||||
|
||||
async printInstallationComplete(projectUrl: string): Promise<void> {
|
||||
logger.info(
|
||||
boxen(
|
||||
"Automatic installation isn't currently supported for Express. \nFollow the steps in our manual installation guide: https://trigger.dev/docs/documentation/guides/manual/express",
|
||||
{ padding: 1, margin: 1, borderStyle: "double", borderColor: "magenta" }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
defaultHostnames = ["localhost", "[::]"];
|
||||
defaultPorts = [3000, 8000, 80, 8080];
|
||||
watchFilePaths = standardWatchFilePaths;
|
||||
watchIgnoreRegex = /(node_modules)/;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { InstallPackage } from "../utils/addDependencies";
|
||||
import { PackageManager } from "../utils/getUserPkgManager";
|
||||
import { Astro } from "./astro";
|
||||
import { Express } from "./express";
|
||||
import { NextJs } from "./nextjs";
|
||||
import { Remix } from "./remix";
|
||||
|
||||
@@ -32,6 +33,9 @@ export interface Framework {
|
||||
/** You can check for middleware, add extra instructions, etc */
|
||||
postInstall(path: string, options: ProjectInstallOptions): Promise<void>;
|
||||
|
||||
/** You can (optionally) override the initComplete messages */
|
||||
printInstallationComplete?(projectUrl: string): Promise<void>;
|
||||
|
||||
/** Used by the dev command, if a hostname isn't passed in */
|
||||
defaultHostnames: string[];
|
||||
|
||||
@@ -46,7 +50,7 @@ export interface Framework {
|
||||
}
|
||||
|
||||
/** The order of these matters. The first one that matches the folder will be used, so stricter ones should be first. */
|
||||
const frameworks: Framework[] = [new NextJs(), new Remix(), new Astro()];
|
||||
const frameworks: Framework[] = [new NextJs(), new Remix(), new Astro(), new Express()];
|
||||
|
||||
export const getFramework = async (
|
||||
path: string,
|
||||
|
||||
@@ -34,6 +34,6 @@
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+3
-13
@@ -639,6 +639,7 @@ importers:
|
||||
'@types/mock-fs': ^4.13.1
|
||||
'@types/node': '16'
|
||||
'@types/node-fetch': ^2.6.2
|
||||
boxen: ^7.1.1
|
||||
chalk: ^5.2.0
|
||||
chokidar: ^3.5.3
|
||||
commander: ^9.4.1
|
||||
@@ -670,6 +671,7 @@ importers:
|
||||
zod: 3.21.4
|
||||
dependencies:
|
||||
'@types/degit': 2.8.3
|
||||
boxen: 7.1.1
|
||||
chalk: 5.2.0
|
||||
chokidar: 3.5.3
|
||||
commander: 9.5.0
|
||||
@@ -14639,7 +14641,7 @@ packages:
|
||||
/axios/0.21.4_debug@4.3.2:
|
||||
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.2_debug@4.3.2
|
||||
follow-redirects: 1.15.2
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
@@ -19183,18 +19185,6 @@ packages:
|
||||
debug:
|
||||
optional: true
|
||||
|
||||
/follow-redirects/1.15.2_debug@4.3.2:
|
||||
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
dependencies:
|
||||
debug: 4.3.2
|
||||
dev: false
|
||||
|
||||
/for-each/0.3.3:
|
||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user