├── .eslintrc.json ├── public ├── logo.png ├── assets │ └── step-icons │ │ ├── ICONS.md │ │ ├── set.svg │ │ ├── jsonValue.svg │ │ ├── if.svg │ │ ├── equation.svg │ │ ├── httpRequest.svg │ │ ├── for.svg │ │ ├── random.svg │ │ ├── log.svg │ │ └── arrayLength.svg └── favicon.ico ├── .github ├── cover.png ├── preview.webp └── workflows │ └── main.yml ├── src ├── lib │ ├── apiClient │ │ ├── ExecuteEndpointRequest.ts │ │ ├── ExecuteEndpointResponse.ts │ │ ├── CreateEndpointRequest.ts │ │ ├── UpdateEndpointRequest.ts │ │ └── ApiClient.ts │ ├── core │ │ └── UidGenerator.ts │ ├── repositories │ │ ├── endpoint │ │ │ ├── EndpointJSON.ts │ │ │ ├── EndpointRepository.ts │ │ │ ├── MemoryEndpointRepository.ts │ │ │ ├── EndpointValidator.ts │ │ │ ├── Endpoint.ts │ │ │ └── MongoEndpointRepository.ts │ │ ├── StorageIdGenerator.ts │ │ ├── storageInstaller.ts │ │ ├── MongoProvider.ts │ │ └── storage.ts │ └── workflows │ │ ├── endpoint │ │ ├── templates │ │ │ ├── EndpointTemplate.ts │ │ │ ├── endpointTemplates.ts │ │ │ ├── usdPlnPriceEndpointTemplate.ts │ │ │ ├── bmiCalculatorEndpointTemplate.ts │ │ │ ├── guessNumberEndpointTemplate.ts │ │ │ └── piApproximationEndpointTemplate.ts │ │ ├── machine │ │ │ ├── services │ │ │ │ ├── LoggerService.ts │ │ │ │ ├── VariablesService.ts │ │ │ │ ├── DynamicsService.ts │ │ │ │ └── EvaluatorService.ts │ │ │ ├── EndpointWorkflowGlobalState.ts │ │ │ ├── activities │ │ │ │ ├── primitives │ │ │ │ │ ├── randomActivity.ts │ │ │ │ │ ├── equationActivity.ts │ │ │ │ │ └── setActivity.ts │ │ │ │ ├── json │ │ │ │ │ ├── arrayLengthActivity.ts │ │ │ │ │ └── jsonValueActivity.ts │ │ │ │ ├── tracing │ │ │ │ │ └── logActivity.ts │ │ │ │ ├── flow │ │ │ │ │ ├── ifActivity.ts │ │ │ │ │ └── forActivity.ts │ │ │ │ └── requests │ │ │ │ │ └── httpRequestActivity.ts │ │ │ ├── activitySet.ts │ │ │ └── EndpointWorkflowMachine.ts │ │ └── model │ │ │ ├── EndpointDefinition.ts │ │ │ ├── modelSet.ts │ │ │ ├── endpointDefinitionModel.ts │ │ │ ├── steps │ │ │ ├── tracing │ │ │ │ └── logStepModel.ts │ │ │ ├── primitives │ │ │ │ ├── setStepModel.ts │ │ │ │ ├── randomStepModel.ts │ │ │ │ └── equationStepModel.ts │ │ │ ├── json │ │ │ │ ├── arrayLengthStepModel.ts │ │ │ │ └── jsonValueStepModel.ts │ │ │ ├── requests │ │ │ │ └── httpRequestStepModel.ts │ │ │ └── flow │ │ │ │ ├── forStepModel.ts │ │ │ │ └── ifStepModel.ts │ │ │ └── StepNameFormatter.ts │ │ └── UserInputParser.ts ├── app │ ├── api │ │ ├── health │ │ │ └── route.ts │ │ ├── endpoints │ │ │ ├── route.ts │ │ │ └── [id] │ │ │ │ └── route.ts │ │ └── functions │ │ │ └── [url] │ │ │ └── route.ts │ ├── endpoints │ │ ├── loading.tsx │ │ ├── [id] │ │ │ ├── edit │ │ │ │ ├── loading.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── EditEndpointPage.tsx │ │ │ └── test │ │ │ │ ├── loading.tsx │ │ │ │ ├── TestEndpointPage.tsx │ │ │ │ └── page.tsx │ │ ├── create │ │ │ ├── page.tsx │ │ │ └── CreateEndpointPage.tsx │ │ ├── page.tsx │ │ └── EndpointListPage.tsx │ ├── page.tsx │ ├── not-found.tsx │ └── layout.tsx ├── components │ ├── theme │ │ ├── layout │ │ │ ├── Root.tsx │ │ │ ├── Main.tsx │ │ │ ├── CenteredBox.tsx │ │ │ └── Navbar.tsx │ │ ├── primitives │ │ │ ├── InputError.tsx │ │ │ ├── Textarea.tsx │ │ │ ├── Input.tsx │ │ │ ├── Button.tsx │ │ │ └── Dropdown.tsx │ │ ├── error │ │ │ └── Error.tsx │ │ ├── endpoint │ │ │ ├── OverviewTabForm.tsx │ │ │ ├── TestTabForm.tsx │ │ │ └── EndpointTabHost.tsx │ │ ├── loading │ │ │ └── Loading.tsx │ │ ├── home │ │ │ └── Home.tsx │ │ └── endpointList │ │ │ └── EndpointList.tsx │ ├── layout │ │ ├── FullscreenLayout.tsx │ │ ├── LoadingDefaultLayout.tsx │ │ └── DefaultLayout.tsx │ └── endpoint │ │ ├── EndpointModes.ts │ │ ├── editor │ │ ├── DesignTabState.ts │ │ ├── OverviewTab.tsx │ │ ├── OverviewTabState.ts │ │ ├── DesignTab.tsx │ │ └── EndpointEditor.tsx │ │ ├── StartDefinitionActivator.ts │ │ └── tester │ │ ├── TestTabState.ts │ │ ├── EndpointExecutor.ts │ │ ├── TestTab.tsx │ │ └── EndpointTester.tsx └── styles │ └── global.css ├── next.config.js ├── postcss.config.js ├── .prettierrc ├── .pnpmfile.cjs ├── .editorconfig ├── tailwind.config.js ├── .gitignore ├── .env.example ├── tsconfig.json ├── LICENSE ├── package.json └── README.md /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nocode-js/nocode-platform-boilerplate/HEAD/public/logo.png -------------------------------------------------------------------------------- /.github/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nocode-js/nocode-platform-boilerplate/HEAD/.github/cover.png -------------------------------------------------------------------------------- /public/assets/step-icons/ICONS.md: -------------------------------------------------------------------------------- 1 | All icons are from [Material set](https://fonts.google.com/icons). 2 | -------------------------------------------------------------------------------- /.github/preview.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nocode-js/nocode-platform-boilerplate/HEAD/.github/preview.webp -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nocode-js/nocode-platform-boilerplate/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/lib/apiClient/ExecuteEndpointRequest.ts: -------------------------------------------------------------------------------- 1 | export type ExecuteEndpointRequest = Record; 2 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "semi": true, 6 | "arrowParens": "avoid" 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/apiClient/ExecuteEndpointResponse.ts: -------------------------------------------------------------------------------- 1 | export interface ExecuteEndpointResponse { 2 | __logs: string[]; 3 | [key: string]: unknown; 4 | } 5 | -------------------------------------------------------------------------------- /.pnpmfile.cjs: -------------------------------------------------------------------------------- 1 | function readPackage(pkg) { 2 | return pkg; 3 | } 4 | 5 | module.exports = { 6 | hooks: { 7 | readPackage 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/app/api/health/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | export async function GET() { 4 | return NextResponse.json({ status: 'OK' }); 5 | } 6 | -------------------------------------------------------------------------------- /src/components/theme/layout/Root.tsx: -------------------------------------------------------------------------------- 1 | export function Root(props: { children: React.ReactNode }) { 2 | return
{props.children}
; 3 | } 4 | -------------------------------------------------------------------------------- /src/lib/apiClient/CreateEndpointRequest.ts: -------------------------------------------------------------------------------- 1 | import { UpdateEndpointRequest } from './UpdateEndpointRequest'; 2 | 3 | export type CreateEndpointRequest = UpdateEndpointRequest; 4 | -------------------------------------------------------------------------------- /src/lib/core/UidGenerator.ts: -------------------------------------------------------------------------------- 1 | import { v4 } from 'uuid'; 2 | 3 | export class UidGenerator { 4 | public static readonly next = (): string => { 5 | return v4(); 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/repositories/endpoint/EndpointJSON.ts: -------------------------------------------------------------------------------- 1 | export interface EndpointJSON { 2 | id: string; 3 | name: string; 4 | url: string; 5 | description: string; 6 | definition: string; 7 | } 8 | -------------------------------------------------------------------------------- /public/assets/step-icons/set.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/endpoints/loading.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingDefaultLayout } from '@/components/layout/LoadingDefaultLayout'; 2 | 3 | export default function Loading() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/theme/layout/Main.tsx: -------------------------------------------------------------------------------- 1 | export function Main(props: { children: React.ReactNode }) { 2 | return
{props.children}
; 3 | } 4 | -------------------------------------------------------------------------------- /src/lib/workflows/endpoint/templates/EndpointTemplate.ts: -------------------------------------------------------------------------------- 1 | import { EndpointJSON } from '@/lib/repositories/endpoint/EndpointJSON'; 2 | 3 | export type EndpointTemplate = () => Omit; 4 | -------------------------------------------------------------------------------- /src/app/endpoints/[id]/edit/loading.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingDefaultLayout } from '@/components/layout/LoadingDefaultLayout'; 2 | 3 | export default function Loading() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/endpoints/[id]/test/loading.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingDefaultLayout } from '@/components/layout/LoadingDefaultLayout'; 2 | 3 | export default function Loading() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /src/styles/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html, 6 | body, 7 | #__next { 8 | height: 100%; 9 | } 10 | 11 | .sqd-designer-react { 12 | height: 100%; 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/repositories/StorageIdGenerator.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from 'mongodb'; 2 | 3 | export class StorageIdGenerator { 4 | public static readonly next = (): string => { 5 | return new ObjectId().toHexString(); 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /src/components/layout/FullscreenLayout.tsx: -------------------------------------------------------------------------------- 1 | export interface FullscreenLayoutProps { 2 | children: React.ReactNode; 3 | } 4 | 5 | export function FullscreenLayout(props: FullscreenLayoutProps) { 6 | return <>{props.children}; 7 | } 8 | -------------------------------------------------------------------------------- /public/assets/step-icons/jsonValue.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/assets/step-icons/if.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/lib/apiClient/UpdateEndpointRequest.ts: -------------------------------------------------------------------------------- 1 | import { EndpointDefinition } from '../workflows/endpoint/model/EndpointDefinition'; 2 | 3 | export interface UpdateEndpointRequest { 4 | name: string; 5 | url: string; 6 | description: string; 7 | definition: EndpointDefinition; 8 | } 9 | -------------------------------------------------------------------------------- /public/assets/step-icons/equation.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/theme/primitives/InputError.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'react'; 2 | 3 | export function InputError(props: { error: string | null | undefined }) { 4 | if (!props.error) { 5 | return ; 6 | } 7 | return
{props.error}
; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/layout/LoadingDefaultLayout.tsx: -------------------------------------------------------------------------------- 1 | import { Loading } from '../theme/loading/Loading'; 2 | import { DefaultLayout } from './DefaultLayout'; 3 | 4 | export function LoadingDefaultLayout() { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | tab_width = 2 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.ts] 11 | quote_type = single 12 | 13 | [*.md] 14 | indent_style = space 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './src/pages/**/*.{js,ts,jsx,tsx,mdx}', 5 | './src/components/**/*.{js,ts,jsx,tsx,mdx}', 6 | './src/app/**/*.{js,ts,jsx,tsx,mdx}', 7 | ], 8 | theme: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /src/components/theme/error/Error.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '../primitives/Button'; 2 | 3 | export function Error(props: { message: string }) { 4 | return ( 5 |
6 |

{props.message}

7 | 8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/workflows/endpoint/machine/services/LoggerService.ts: -------------------------------------------------------------------------------- 1 | export class LoggerService { 2 | private readonly logs: string[] = []; 3 | 4 | public log(message: string): void { 5 | this.logs.push(message); 6 | } 7 | 8 | public popAll(): string[] { 9 | const logs = [...this.logs]; 10 | this.logs.length = 0; 11 | return logs; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/theme/layout/CenteredBox.tsx: -------------------------------------------------------------------------------- 1 | export interface CenteredBoxProps { 2 | children: React.ReactNode; 3 | className?: string; 4 | } 5 | 6 | export function CenteredBox(props: CenteredBoxProps) { 7 | let cls = 'mx-auto max-w-7xl'; 8 | if (props.className) { 9 | cls += ` ${props.className}`; 10 | } 11 | return
{props.children}
; 12 | } 13 | -------------------------------------------------------------------------------- /public/assets/step-icons/httpRequest.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/lib/workflows/endpoint/model/EndpointDefinition.ts: -------------------------------------------------------------------------------- 1 | import { VariableDefinitions } from 'sequential-workflow-editor-model'; 2 | import { Definition } from 'sequential-workflow-model'; 3 | 4 | export interface EndpointDefinition extends Definition { 5 | properties: { 6 | inputs: VariableDefinitions; 7 | internals: VariableDefinitions; 8 | outputs: VariableDefinitions; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { DefaultLayout } from '@/components/layout/DefaultLayout'; 2 | import { Home } from '@/components/theme/home/Home'; 3 | import { Metadata } from 'next'; 4 | 5 | export const metadata: Metadata = { 6 | title: 'NoCode API Builder Template' 7 | }; 8 | 9 | export default function Page() { 10 | return ( 11 | 12 | 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /src/app/endpoints/create/page.tsx: -------------------------------------------------------------------------------- 1 | import { DefaultLayout } from '@/components/layout/DefaultLayout'; 2 | import { CreateEndpointPage } from './CreateEndpointPage'; 3 | import { Metadata } from 'next'; 4 | 5 | export const metadata: Metadata = { 6 | title: 'Create endpoint' 7 | }; 8 | 9 | export default function Page() { 10 | return ( 11 | 12 | 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # next.js 10 | /.next/ 11 | /out/ 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # local env files 26 | .env*.local 27 | .env 28 | 29 | # vercel 30 | .vercel 31 | 32 | # typescript 33 | *.tsbuildinfo 34 | next-env.d.ts 35 | -------------------------------------------------------------------------------- /src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { FullscreenLayout } from '@/components/layout/FullscreenLayout'; 2 | import { Error } from '@/components/theme/error/Error'; 3 | import { Metadata } from 'next'; 4 | 5 | export const metadata: Metadata = { 6 | title: 'Page not found' 7 | }; 8 | 9 | export default function NotFound() { 10 | return ( 11 | 12 | 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /public/assets/step-icons/for.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/endpoint/EndpointModes.ts: -------------------------------------------------------------------------------- 1 | import { EndpointTabHostMode } from '../theme/endpoint/EndpointTabHost'; 2 | 3 | export type EndpointMode = 'edit' | 'test'; 4 | 5 | export function createEndpointModes(current: EndpointMode, isNewMode: boolean): EndpointTabHostMode[] { 6 | return [ 7 | { 8 | id: 'edit', 9 | label: isNewMode ? 'Create' : 'Edit', 10 | isSelected: current === 'edit' 11 | }, 12 | { 13 | id: 'test', 14 | label: 'Test', 15 | isSelected: current === 'test' 16 | } 17 | ]; 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/repositories/endpoint/EndpointRepository.ts: -------------------------------------------------------------------------------- 1 | import { Endpoint } from './Endpoint'; 2 | 3 | export interface EndpointRepository { 4 | insert(endpoint: Endpoint): Promise; 5 | update(endpoint: Endpoint): Promise; 6 | /** 7 | * @returns true if the endpoint was found and deleted, false if the endpoint was not found. 8 | */ 9 | tryDeleteById(endpoint: string): Promise; 10 | getAll(): Promise; 11 | tryGetById(id: string): Promise; 12 | tryGetByUrl(url: string): Promise; 13 | } 14 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # The app supports two storage types: memory and mongodb. 2 | # Copy this file to `.env` and set STORAGE_TYPE and other variables depending on the storage type you want to use. 3 | 4 | ################################### 5 | #### Memory mode (default) #### 6 | ################################### 7 | 8 | STORAGE_TYPE=memory 9 | 10 | ################################### 11 | #### MongoDb mode #### 12 | ################################### 13 | 14 | STORAGE_TYPE=mongodb 15 | MONGODB_URI=mongodb://localhost:27017 16 | MONGODB_DB=nocodeApiBuilder 17 | -------------------------------------------------------------------------------- /src/components/theme/primitives/Textarea.tsx: -------------------------------------------------------------------------------- 1 | export interface TextareaProps { 2 | value: string; 3 | onChanged: (value: string) => void; 4 | rows?: number; 5 | } 6 | 7 | export function Textarea(props: TextareaProps) { 8 | return ( 9 |