diff --git a/README.md b/README.md index 05ff5d17b..e318094de 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@
-
@@ -17,124 +17,133 @@
Documentation
+### Built-in Dashboard
+Visualize requests, responses, and logs in real-time at http://localhost:8080
+
-# 🧪 Learn by Example
+### Multiple Providers
+Load specs from local files, HTTP URLs, Git repositories, or NPM packages.
-Explore tutorials that walk you through mocking different protocols and scenarios:
+## Common Use Cases
-- 🌍 [Get started with REST API](https://mokapi.io/resources/tutorials/get-started-with-rest-api)\
- This tutorial will show you how to mock a REST API using an OpenAPI specification.
+### Frontend Development
+Mock backend APIs while building UIs. Test loading states, errors, and edge cases without waiting for real endpoints.
-- ⚡ [Mocking Kafka with AsyncAPI](https://mokapi.io/resources/tutorials/get-started-with-kafka)\
- Mocking a Kafka topic using Mokapi and verifying that a producer generates valid messages.
+### API Testing
+Simulate timeouts, 500 errors, rate limits, and malformed responses. Test how your application handles failures.
-- 👨💻 [Mocking LDAP Authentication](https://mokapi.io/resources/tutorials/mock-ldap-authentication-in-node)\
- Simulate LDAP-based login flows, including group-based permissions.
+### CI/CD Integration
+Run fast, reliable tests without external dependencies. No flaky tests due to network issues or unavailable services.
-- 📧 [Mocking SMTP Mail Servers](https://mokapi.io/resources/tutorials/mock-smtp-server-send-mail-using-node)\
- Use Mokapi to simulate sending and receiving emails in Node.js apps.
+### Contract Validation
+Validate that your requests and responses match your OpenAPI specification. Catch breaking changes early.
-- 🖥️ [End-to-End Testing with Jest and GitHub Actions](https://mokapi.io/resources/tutorials/running-mokapi-in-a-ci-cd-pipeline)\
- Integrate Mokapi into your CI pipeline for full-stack E2E testing.
+# Example Tutorials
-> More examples are available on [mokapi.io/resources](https://mokapi.io/resources)
+Explore tutorials that walk you through mocking different protocols and scenarios:
+
+- [Get started with REST API](https://mokapi.io/resources/tutorials/get-started-with-rest-api)
+ Mock a REST API using OpenAPI specification
+
+- [Mock Kafka with AsyncAPI](https://mokapi.io/resources/tutorials/get-started-with-kafka)
+ Simulate Kafka topics and validate message producers
+
+- [Mock LDAP Authentication](https://mokapi.io/resources/tutorials/mock-ldap-authentication-in-node)\
+ Test authentication flows without a real LDAP server
-# 📚 Documentation
+- [Mock SMTP Mail Servers](https://mokapi.io/resources/tutorials/mock-smtp-server-send-mail-using-node)\
+ Test email workflows without sending real messages
-- [Get Started](https://mokapi.io/docs/welcome)
-- [HTTP](https://mokapi.io/docs/http/overview)
-- [Kafka](https://mokapi.io/docs/kafka/overview)
-- [LDAP](https://mokapi.io/docs/ldap/overview)
-- [SMTP](https://mokapi.io/docs/mail/overview)
-- [Javascript API](https://mokapi.io/docs/javascript-api/overview)
-- [Resources](https://mokapi.io/resources)
+- [CI/CD Integration with GitHub Actions](https://mokapi.io/resources/tutorials/running-mokapi-in-a-ci-cd-pipeline)\
+ Run Mokapi in automated test pipelines
-# ☕ Support
+> More examples [mokapi.io/resources](https://mokapi.io/resources)
-Show your love for Mokapi and support the project by grabbing some cool merch!
-[Visit the Mokapi Merchandise Store](https://mokapi.myspreadshop.ch) 🔥
+## Documentation
-If you like Mokapi, consider buying me a coffee:
+- [Getting Started Guide](https://mokapi.io/docs/welcome)
+- [HTTP/REST API Documentation](https://mokapi.io/docs/http/overview)
+- [Kafka Documentation](https://mokapi.io/docs/kafka/overview)
+- [LDAP Documentation](https://mokapi.io/docs/ldap/overview)
+- [SMTP/Mail Documentation](https://mokapi.io/docs/mail/overview)
+- [JavaScript API Reference](https://mokapi.io/docs/javascript-api/overview)
+- [Configuration Guide](https://mokapi.io/docs/configuration/overview)
+
+## Support the Project
+
+If Mokapi helps your team ship faster, consider supporting development:
-# 📄 License
+## License
+
+MIT License - see [LICENSE](https://github.com/marle3003/mokapi/blob/main/LICENSE) for details.
+
+## Links
-This project is licensed under the MIT License. See the [LICENSE](https://github.com/marle3003/mokapi/blob/main/LICENSE) file for details.
\ No newline at end of file
+- Website: [mokapi.io](https://mokapi.io)
+- GitHub: [github.com/marle3003/mokapi](https://github.com/marle3003/mokapi)
+- NPM Package: [npmjs.com/package/go-mokapi](https://npmjs.com/package/go-mokapi)
+- Documentation: [mokapi.io/docs](https://mokapi.io/docs)
+- Tutorials: [mokapi.io/resources/tutorials](https://mokapi.io/resources/tutorials)
+- Blog: [mokapi.io/resources/blogs](https://mokapi.io/resources/blogs)
\ No newline at end of file
diff --git a/api/handler_fileserver_test.go b/api/handler_fileserver_test.go
index f554e48c1..323ee6feb 100644
--- a/api/handler_fileserver_test.go
+++ b/api/handler_fileserver_test.go
@@ -2,7 +2,6 @@ package api_test
import (
"fmt"
- "github.com/stretchr/testify/require"
"mokapi/api"
"mokapi/config/dynamic"
"mokapi/config/static"
@@ -14,6 +13,8 @@ import (
"net/url"
"strings"
"testing"
+
+ "github.com/stretchr/testify/require"
)
func TestHandler_FileServer(t *testing.T) {
@@ -168,7 +169,7 @@ func TestOpenGraphInDashboard(t *testing.T) {
app := runtime.New(cfg)
app.AddHttp(&dynamic.Config{Info: dynamic.ConfigInfo{Url: mustParse("https://foo.bar")}, Data: openapitest.NewConfig("3.0",
openapitest.WithInfo("Swagger Petstore", "1.0", "This is a sample server Petstore server."),
- openapitest.WithPath("/pet/{petId}", openapitest.NewPath()),
+ openapitest.WithPath("/pet/{petId}"),
)},
)
h := api.New(app, static.Api{Path: "/mokapi", Dashboard: true})
@@ -192,9 +193,9 @@ func TestOpenGraphInDashboard(t *testing.T) {
app := runtime.New(cfg)
app.AddHttp(&dynamic.Config{Info: dynamic.ConfigInfo{Url: mustParse("https://foo.bar")}, Data: openapitest.NewConfig("3.0",
openapitest.WithInfo("Swagger Petstore", "1.0", "This is a sample server Petstore server."),
- openapitest.WithPath("/pet/{petId}", openapitest.NewPath(
+ openapitest.WithPath("/pet/{petId}",
openapitest.WithPathInfo("foo", "bar"),
- )),
+ ),
),
})
h := api.New(app, static.Api{Path: "/mokapi", Dashboard: true})
@@ -218,9 +219,9 @@ func TestOpenGraphInDashboard(t *testing.T) {
app := runtime.New(cfg)
app.AddHttp(&dynamic.Config{Info: dynamic.ConfigInfo{Url: mustParse("https://foo.bar")}, Data: openapitest.NewConfig("3.0",
openapitest.WithInfo("Swagger Petstore", "1.0", "This is a sample server Petstore server."),
- openapitest.WithPath("/pet/{petId}", openapitest.NewPath(
+ openapitest.WithPath("/pet/{petId}",
openapitest.WithPathInfo("", "bar"),
- ))),
+ )),
})
h := api.New(app, static.Api{Path: "/mokapi", Dashboard: true})
try.Handler(t,
@@ -243,9 +244,9 @@ func TestOpenGraphInDashboard(t *testing.T) {
app := runtime.New(cfg)
app.AddHttp(&dynamic.Config{Info: dynamic.ConfigInfo{Url: mustParse("https://foo.bar")}, Data: openapitest.NewConfig("3.0",
openapitest.WithInfo("Swagger Petstore", "1.0", "This is a sample server Petstore server."),
- openapitest.WithPath("/pet/{petId}", openapitest.NewPath(
- openapitest.WithOperation("GET", openapitest.NewOperation()),
- ))),
+ openapitest.WithPath("/pet/{petId}",
+ openapitest.WithOperation("GET"),
+ )),
})
h := api.New(app, static.Api{Path: "/mokapi", Dashboard: true})
try.Handler(t,
@@ -268,12 +269,13 @@ func TestOpenGraphInDashboard(t *testing.T) {
app := runtime.New(cfg)
app.AddHttp(&dynamic.Config{Info: dynamic.ConfigInfo{Url: mustParse("https://foo.bar")}, Data: openapitest.NewConfig("3.0",
openapitest.WithInfo("Swagger Petstore", "1.0", "This is a sample server Petstore server."),
- openapitest.WithPath("/pet/{petId}", openapitest.NewPath(
- openapitest.WithOperation("GET", openapitest.NewOperation()),
- )),
- openapitest.WithPath("/pet/{petId}/foo", openapitest.NewPath(
- openapitest.WithOperation("GET", openapitest.NewOperation()),
- ))),
+ openapitest.WithPath("/pet/{petId}",
+ openapitest.WithOperation("GET"),
+ ),
+ openapitest.WithPath("/pet/{petId}/foo",
+ openapitest.WithOperation("GET"),
+ ),
+ ),
})
h := api.New(app, static.Api{Path: "/mokapi", Dashboard: true})
try.Handler(t,
diff --git a/api/handler_http_test.go b/api/handler_http_test.go
index 756af9fcb..460636508 100644
--- a/api/handler_http_test.go
+++ b/api/handler_http_test.go
@@ -111,10 +111,10 @@ func TestHandler_Http(t *testing.T) {
return runtimetest.NewHttpApp(
openapitest.NewConfig("3.0.0",
openapitest.WithInfo("foo", "", ""),
- openapitest.WithPath("/foo/{bar}", openapitest.NewPath(
+ openapitest.WithPath("/foo/{bar}",
openapitest.WithPathParam("bar", openapitest.WithParamSchema(schematest.New("string"))),
- openapitest.WithOperation("get", openapitest.NewOperation()),
- )),
+ openapitest.WithOperation("get"),
+ ),
),
)
},
@@ -127,13 +127,13 @@ func TestHandler_Http(t *testing.T) {
return runtimetest.NewHttpApp(
openapitest.NewConfig("3.0.0",
openapitest.WithInfo("foo", "", ""),
- openapitest.WithPath("/foo/{bar}", openapitest.NewPath(
- openapitest.WithOperation("get", openapitest.NewOperation(
+ openapitest.WithPath("/foo/{bar}",
+ openapitest.WithOperation("get",
openapitest.WithRequestBody("foo", true,
openapitest.WithRequestContent("application/json", openapitest.NewContent(openapitest.WithSchema(schematest.New("string")))),
),
- )),
- ))),
+ ),
+ )),
)
},
requestUrl: "http://foo.api/api/services/http/foo",
@@ -145,11 +145,11 @@ func TestHandler_Http(t *testing.T) {
return runtimetest.NewHttpApp(
openapitest.NewConfig("3.0.0",
openapitest.WithInfo("foo", "", ""),
- openapitest.WithPath("/foo", openapitest.NewPath(
- openapitest.WithOperation("get", openapitest.NewOperation(
+ openapitest.WithPath("/foo",
+ openapitest.WithOperation("get",
openapitest.WithSecurity(map[string][]string{"foo": {}}),
- )),
- )),
+ ),
+ ),
openapitest.WithComponentSecurity("foo", &openapi.ApiKeySecurityScheme{
Type: "apiKey",
In: "header",
@@ -168,9 +168,9 @@ func TestHandler_Http(t *testing.T) {
openapitest.NewConfig("3.0.0",
openapitest.WithGlobalSecurity(map[string][]string{"foo": {}}),
openapitest.WithInfo("foo", "", ""),
- openapitest.WithPath("/foo", openapitest.NewPath(
- openapitest.WithOperation("get", openapitest.NewOperation()),
- )),
+ openapitest.WithPath("/foo",
+ openapitest.WithOperation("get"),
+ ),
openapitest.WithComponentSecurity("foo", &openapi.ApiKeySecurityScheme{
Type: "apiKey",
In: "header",
@@ -188,20 +188,17 @@ func TestHandler_Http(t *testing.T) {
return runtimetest.NewHttpApp(
openapitest.NewConfig("3.0.0",
openapitest.WithInfo("foo", "", ""),
- openapitest.WithPath("/foo/{bar}", openapitest.NewPath(
- openapitest.WithOperation("get", openapitest.NewOperation(
+ openapitest.WithPath("/foo/{bar}",
+ openapitest.WithOperation("get",
openapitest.WithResponse(http.StatusOK,
openapitest.WithResponseDescription("foo description"),
openapitest.WithContent(
- "application/json",
- openapitest.NewContent(
- openapitest.WithSchema(schematest.New("string")),
- ),
- ),
+ "application/json", openapitest.WithSchema(schematest.New("string"))),
openapitest.WithResponseHeader("foo", "bar", schematest.New("string")),
),
- )),
- ))),
+ ),
+ ),
+ ),
)
},
requestUrl: "http://foo.api/api/services/http/foo",
@@ -220,8 +217,8 @@ func TestHandler_Http(t *testing.T) {
},
Value: openapitest.NewPath(
openapitest.WithPathInfo("foo", "bar"),
- openapitest.WithOperation("get", openapitest.NewOperation(
- openapitest.WithResponseRef(http.StatusOK,
+ openapitest.WithOperation("get",
+ openapitest.UseResponseRef(http.StatusOK,
&openapi.ResponseRef{
Reference: dynamic.Reference{
Ref: "#/components/pathItems/foo",
@@ -229,16 +226,13 @@ func TestHandler_Http(t *testing.T) {
},
Value: openapitest.NewResponse(openapitest.WithResponseDescription("foo description"),
openapitest.WithContent(
- "application/json",
- openapitest.NewContent(
- openapitest.WithSchema(schematest.New("string")),
- ),
+ "application/json", openapitest.WithSchema(schematest.New("string")),
),
openapitest.WithResponseHeader("foo", "bar", schematest.New("string")),
),
},
),
- )),
+ ),
),
}),
)
@@ -254,19 +248,17 @@ func TestHandler_Http(t *testing.T) {
return runtimetest.NewHttpApp(
openapitest.NewConfig("3.0.0",
openapitest.WithInfo("foo", "", ""),
- openapitest.WithPath("/foo/{bar}", openapitest.NewPath(
- openapitest.WithOperation("get", openapitest.NewOperation(
+ openapitest.WithPath("/foo/{bar}",
+ openapitest.WithOperation("get",
openapitest.WithResponse(http.StatusOK,
openapitest.WithResponseDescription("foo description"),
openapitest.WithContent(
- "application/json",
- openapitest.NewContent(
- openapitest.WithSchema(schematest.New("string", schematest.And("number"))),
- ),
+ "application/json", openapitest.WithSchema(schematest.New("string", schematest.And("number"))),
),
),
- )),
- ))),
+ ),
+ ),
+ ),
)
},
requestUrl: "http://foo.api/api/services/http/foo",
@@ -278,19 +270,16 @@ func TestHandler_Http(t *testing.T) {
return runtimetest.NewHttpApp(
openapitest.NewConfig("3.0.0",
openapitest.WithInfo("foo", "", ""),
- openapitest.WithPath("/foo/{bar}", openapitest.NewPath(
- openapitest.WithOperation("get", openapitest.NewOperation(
+ openapitest.WithPath("/foo/{bar}",
+ openapitest.WithOperation("get",
openapitest.WithResponse(http.StatusOK,
openapitest.WithResponseDescription("foo description"),
openapitest.WithContent(
- "application/json",
- openapitest.NewContent(
- openapitest.WithSchema(schematest.New("string", schematest.WithDefault("foobar"))),
- ),
+ "application/json", openapitest.WithSchema(schematest.New("string", schematest.WithDefault("foobar"))),
),
),
)),
- ))),
+ ),
)
},
requestUrl: "http://foo.api/api/services/http/foo",
@@ -302,11 +291,11 @@ func TestHandler_Http(t *testing.T) {
return runtimetest.NewHttpApp(
openapitest.NewConfig("3.0.0",
openapitest.WithInfo("foo", "", ""),
- openapitest.WithPath("/foo/{bar}", openapitest.NewPath(
- openapitest.WithOperation("get", openapitest.NewOperation(
+ openapitest.WithPath("/foo/{bar}",
+ openapitest.WithOperation("get",
openapitest.WithTagName("foo"),
- )),
- )),
+ ),
+ ),
openapitest.WithTag("foo", "sum", "desc"),
),
)
diff --git a/api/handler_schema_test.go b/api/handler_schema_test.go
index 1a59d19bc..3f07355fb 100644
--- a/api/handler_schema_test.go
+++ b/api/handler_schema_test.go
@@ -35,16 +35,13 @@ func TestHandler_Schema_Example_Query(t *testing.T) {
app: runtimetest.NewHttpApp(openapitest.NewConfig("3.1.0",
openapitest.WithInfo("foo", "", ""),
openapitest.WithPath("/foo",
- openapitest.NewPath(openapitest.WithOperation("GET",
- openapitest.NewOperation(
- openapitest.WithResponse(200,
- openapitest.WithContent("application/json", openapitest.NewContent(
- openapitest.WithSchema(schematest.New("string")),
- )),
+ openapitest.WithOperation("GET",
+ openapitest.WithResponse(200,
+ openapitest.WithContent("application/json",
+ openapitest.WithSchema(schematest.New("string")),
),
),
),
- ),
),
),
),
@@ -94,20 +91,15 @@ func TestHandler_Schema_Example_Query(t *testing.T) {
app: runtimetest.NewHttpApp(openapitest.NewConfig("3.1.0",
openapitest.WithInfo("foo", "", ""),
openapitest.WithPath("/foo",
- openapitest.NewPath(openapitest.WithOperation("GET",
- openapitest.NewOperation(
- openapitest.WithResponse(200,
- openapitest.WithContent("application/json", openapitest.NewContent(
- openapitest.WithSchema(schematest.New("string")),
- )),
- ),
- openapitest.WithResponse(400,
- openapitest.WithContent("application/json", openapitest.NewContent(
- openapitest.WithSchema(schematest.New("string")),
- )),
+ openapitest.WithOperation("GET",
+ openapitest.WithResponse(200,
+ openapitest.WithContent("application/json",
+ openapitest.WithSchema(schematest.New("string")),
),
),
- ),
+ openapitest.WithResponse(400,
+ openapitest.WithContent("application/json", openapitest.WithSchema(schematest.New("string"))),
+ ),
),
),
),
diff --git a/config/dynamic/asyncApi/parsing_test.go b/config/dynamic/asyncApi/parsing_test.go
index e4dded816..ec2556f18 100644
--- a/config/dynamic/asyncApi/parsing_test.go
+++ b/config/dynamic/asyncApi/parsing_test.go
@@ -2,13 +2,14 @@ package asyncApi_test
import (
"fmt"
- "github.com/stretchr/testify/require"
"mokapi/config/dynamic"
"mokapi/config/dynamic/asyncApi"
"mokapi/providers/asyncapi3"
"mokapi/schema/json/schema"
"net/url"
"testing"
+
+ "github.com/stretchr/testify/require"
)
type readFunc func(cfg *dynamic.Config) error
@@ -444,10 +445,8 @@ func TestSchema(t *testing.T) {
t.Run("modify file reference direct", func(t *testing.T) {
target := &schema.Schema{}
message.Payload = &asyncapi3.SchemaRef{Value: &asyncapi3.MultiSchemaFormat{Schema: &schema.Schema{Ref: "foo.yml"}}}
- var fooConfig *dynamic.Config
reader := &testReader{readFunc: func(file *dynamic.Config) error {
- file.Data = &schema.Schema{}
- fooConfig = file
+ file.Data = target
return nil
}}
@@ -455,10 +454,10 @@ func TestSchema(t *testing.T) {
require.NoError(t, err)
// modify
- fooConfig.Data = target
- err = fooConfig.Data.(dynamic.Parser).Parse(fooConfig, reader)
+ target = &schema.Schema{Description: "TARGET"}
+ err = config.Parse(&dynamic.Config{Info: dynamic.ConfigInfo{Url: &url.URL{}}, Data: config}, reader)
require.NoError(t, err)
- require.Equal(t, target, message.Payload.Value.Schema.(*schema.Schema))
+ require.Equal(t, "TARGET", message.Payload.Value.Schema.(*schema.Schema).Description)
})
}
diff --git a/config/dynamic/config.go b/config/dynamic/config.go
index 29cd83296..64024fedf 100644
--- a/config/dynamic/config.go
+++ b/config/dynamic/config.go
@@ -47,6 +47,7 @@ type Config struct {
Listeners Listeners
Scope Scope
SourceType SourceType
+ resolving map[string]bool
}
type Refs struct {
@@ -229,3 +230,19 @@ func (c *Config) CloseScope() {
func (e Event) String() string {
return EventText[e]
}
+
+func (c *Config) EnterRef(ref string) bool {
+ if c.resolving == nil {
+ c.resolving = map[string]bool{}
+ }
+
+ if c.resolving[ref] {
+ return false
+ }
+ c.resolving[ref] = true
+ return true
+}
+
+func (c *Config) LeaveRef(ref string) {
+ delete(c.resolving, ref)
+}
diff --git a/config/dynamic/resolve.go b/config/dynamic/resolve.go
index 0c78e85b2..b15211f32 100644
--- a/config/dynamic/resolve.go
+++ b/config/dynamic/resolve.go
@@ -2,12 +2,13 @@ package dynamic
import (
"fmt"
- log "github.com/sirupsen/logrus"
"mokapi/sortedmap"
"net/url"
"path/filepath"
"reflect"
"strings"
+
+ log "github.com/sirupsen/logrus"
)
type PathResolver interface {
@@ -22,11 +23,14 @@ func Resolve(ref string, element interface{}, config *Config, reader Reader) err
var err error
fragment := ref[1:]
+ isLocal := true
+ parent := config
if !strings.HasPrefix(ref, "#") {
fragment, config, err = resolveResource(ref, element, config, reader)
if err != nil {
return fmt.Errorf("resolve reference '%v' failed: %w", ref, err)
}
+ isLocal = false
}
err = resolveFragment(fragment, element, config, false)
@@ -34,6 +38,29 @@ func Resolve(ref string, element interface{}, config *Config, reader Reader) err
if err != nil {
return fmt.Errorf("resolve reference '%v' failed: %w", ref, err)
}
+
+ // Parse the referenced schema again in the current context.
+ // This ensures nested $ref and $dynamicRef are resolved relative
+ // to the correct dynamic scope.
+ // element is **struct
+ p, ok := reflect.ValueOf(element).Elem().Interface().(Parser)
+ if ok {
+ if !isLocal {
+ // set parent scope hierarchy
+ config = &Config{Raw: config.Raw, Data: copyData(config.Data), Info: config.Info}
+ config.Scope.SetParent(parent.Scope)
+ }
+ if !config.EnterRef(ref) {
+ return nil
+ }
+ defer config.LeaveRef(ref)
+
+ err = p.Parse(config, reader)
+ if err != nil {
+ return fmt.Errorf("resolve reference '%v' failed: %w", ref, err)
+ }
+ }
+
return nil
}
@@ -239,15 +266,6 @@ func resolveResource(ref string, element interface{}, config *Config, reader Rea
sub, err := reader.Read(removeFragment(u), data)
if err == nil {
AddRef(config, sub)
- if _, ok := sub.Data.(Parser); ok && len(sub.Raw) > 0 {
- // parse again with parent scope hierarchy
- sub = &Config{Raw: sub.Raw, Data: copyData(sub.Data), Info: sub.Info}
- sub.Scope.SetParent(config.Scope)
- err = Parse(sub, reader)
- if err != nil {
- return "", nil, err
- }
- }
}
return u.Fragment, sub, err
}
diff --git a/config/static/static_config.go b/config/static/static_config.go
index 1960ffc72..a7b39a434 100644
--- a/config/static/static_config.go
+++ b/config/static/static_config.go
@@ -128,7 +128,7 @@ type HttpProvider struct {
}
type NpmProvider struct {
- GlobalFolders []string `yaml:"globalFolders" name:"global-folders" explode:"global-folders"`
+ GlobalFolders []string `yaml:"globalFolders" name:"global-folders" explode:"global-folder"`
Packages []NpmPackage `explode:"package"`
}
diff --git a/docs/get-started/installation.md b/docs/get-started/installation.md
index f402e131e..fddd823b1 100644
--- a/docs/get-started/installation.md
+++ b/docs/get-started/installation.md
@@ -1,13 +1,41 @@
---
title: "Install Mokapi: Quick & Easy Setup Guide"
-description: Learn how to install Mokapi effortlessly across Windows, macOS, and Linux. Follow the step-by-step guide for a smooth setup experience.
+description: Install Mokapi on Windows, macOS, Linux, Docker, or Node.js. Follow a simple step-by-step guide to get started quickly.
+subtitle: Get Mokapi running in seconds on Windows, macOS, Linux, Docker, or Node.js.
+cards:
+ items:
+ - title: Run Your First Mock
+ href: /docs/get-started/running
+ description: Learn how to start Mokapi and mock your first API
+ - title: Explore Tutorials
+ href: /resources
+ description: Follow step-by-step guides for REST, Kafka, LDAP, and SMTP
+ - title: Write Scripts
+ href: /docs/javascript-api/overview
+ description: Add dynamic behavior to your mocks with JavaScript
+ - title: Configure Mokapi
+ href: /docs/configuration/overview
+ description: Customize ports, providers, and other settings
---
# Install Mokapi
-Mokapi is an open-source tool designed to simplify API mocking and schema validation.
-It enables developers to prototype, test, and demonstrate APIs with realistic data and
-scenarios. This guide provides straightforward instructions to install Mokapi on various
-platforms.
+## Overview
+
+Mokapi is an open-source API mocking tool that helps you develop and test faster by simulating REST APIs, Kafka topics,
+LDAP directories, and SMTP servers. This guide shows you how to install Mokapi on your platform.
+
+Choose your preferred installation method based on your platform and workflow.
+
+```` box=benefits title="Try Without Installing"
+Test Mokapi instantly with npx (requires Node.js):
+
+```bash style=simple
+npx go-mokapi serve https://petstore3.swagger.io/api/v3/openapi.json
+```
+
+
+This starts a mock server immediately without permanent installation. Perfect for quick tests and demos.
+````
## Installation Options
@@ -16,10 +44,23 @@ Choose your preferred method below:
::: tabs
+@tab "NPM"
+
+If you prefer to install Mokapi globally as a Node.js package, install [go-mokapi](https://www.npmjs.com/package/go-mokapi)
+using:
+
+```bash
+npm install -g go-mokapi
+```
+
+After installation, the mokapi command is available globally.
+
@tab "macOS"
### Homebrew
+Install via Homebrew for easy updates and management:
+
```bash
brew tap marle3003/tap
brew install mokapi
@@ -27,12 +68,15 @@ brew install mokapi
### Direct Download
-Download the latest macOS version from [GitHub](https://github.com/marle3003/mokapi/releases)
+Download the latest macOS binary from [GitHub](https://github.com/marle3003/mokapi/releases). Extract
+archive and move the binary to your PATH.
@tab "Windows"
### Chocolatey
+Install via Chocolatey for easy updates:
+
```Powershell
choco install mokapi
```
@@ -43,6 +87,8 @@ Download the latest Windows version from [GitHub](https://github.com/marle3003/m
@tab "Linux"
+Download the .deb package from the releases page and install:
+
### Direct Download
Download file appropriate for your Linux distribution and ARCH from the [release page](https://github.com/marle3003/mokapi/releases), then install with
@@ -57,33 +103,54 @@ rpm -i mokapi_{version}_linux_{arch}.rpm
@tab "Docker"
-To get started with Mokapi using Docker, visit [DockerHub](https://hub.docker.com/r/mokapi/mokapi/tags) for a list of available images.
-You can also use a custom base Docker image as demonstrated in [these examples](/resources/examples/mokapi-with-custom-base-image.md).
+Mokapi provides official Docker images on [Docker Hub](https://hub.docker.com/r/mokapi/mokapi):
```
docker pull mokapi/mokapi
```
-@tab "NPM"
+:::
-If you prefer to install Mokapi as a Node.js package, use the following command:
+```` box=info title="Verify Installation"
+After installation, verify Mokapi is working:
-```bash
-npm install go-mokapi
+```bash style=simple
+mokapi --version
```
-:::
+````
-### Mokapi Scripts Type Definitions
+## TypeScript Support
-Mokapi allows you to write **custom scripts** to handle API events or modify responses.
-For full type safety and autocompletion in TypeScript, you can install the [`@types/mokapi`](https://www.npmjs.com/package/@types/mokapi`) package:
+For full type safety and autocompletion when writing Mokapi scripts in TypeScript, install the type definitions:
```bash
npm install --save-dev @types/mokapi
```
-## Next steps
+This enables IntelliSense and type checking in your IDE when writing custom event handlers and scripts.
+
+### Example TypeScript Script
+
+```typescript title=petstore.ts
+import { on } from 'mokapi'
+
+export default function() {
+ on('http', (request, response) => {
+ // TypeScript provides full autocompletion here
+ if (request.path.petId === '999') {
+ response.statusCode = 404
+ response.data = { message: 'Pet not found' }
+ }
+ })
+}
+```
+``` box=tip title="IDE Integration"
+With @types/mokapi installed, editors like VS Code will provide autocompletion for Mokapi's API, making script development faster and less error-prone.
+```
+
+## What's Next?
+
+Now that Mokapi is installed, here's what you can do:
-- [Create your first Mock](/docs/get-started/running.md)
-- [Install @types/mokapi](https://www.npmjs.com/package/@types/mokapi)
\ No newline at end of file
+{{ card-grid key="cards" }}
\ No newline at end of file
diff --git a/docs/http/overview.md b/docs/http/overview.md
index 8cf1c48ae..9b2068913 100644
--- a/docs/http/overview.md
+++ b/docs/http/overview.md
@@ -56,8 +56,8 @@ Point Mokapi to any publicly accessible OpenAPI specification:
mokapi https://petstore3.swagger.io/api/v3/openapi.json
```
```text box=tip
-Mokapi supports both simplified syntax (`mokapi <url>`) and
-verbose flags (`mokapi --providers-http-url <url>`). This guide uses the
+Mokapi supports both simplified syntax (`mokapi ${token.content}
+ `
+ }
}
diff --git a/webui/src/views/DocsView.vue b/webui/src/views/DocsView.vue
index cc39c78ae..2bb2231d1 100644
--- a/webui/src/views/DocsView.vue
+++ b/webui/src/views/DocsView.vue
@@ -241,11 +241,13 @@ ol.breadcrumb {
.content h1 {
margin-top: 0;
- font-size: 2.25rem;
+ font-size: 2.0rem;
+ font-weight: 900
}
.content h2 {
- font-size: 1.55rem;
+ font-size: 1.75rem;
+ font-weight: 700;
}
.content h2 > * {
@@ -262,7 +264,8 @@ ol.breadcrumb {
}
.content h3 {
- font-size: 1.4rem;
+ font-size: 1.25rem;
+ font-weight: 700;;
}
.content p {
@@ -372,17 +375,15 @@ pre {
margin-left: 0;
}
}
-code {
- background-color: #f1f3f5;
+.content code:not(pre code) {
color: #e63946;
- border-radius: 3px;
- padding: 2px 6px;
+
font-weight: 600
}
-.code {
+.content .code {
margin-bottom: 8px;
}
-.code pre code.hljs {
+.content .code pre code.hljs {
font-family: Menlo,Monaco,Consolas,"Courier New",monospace !important;
padding-left: 0 !important;
}
@@ -393,8 +394,7 @@ code {
}
.box {
- padding: 0.6rem;
- padding-bottom: 0;
+ padding: 16px 20px;
margin-top: 2rem;
margin-bottom: 2rem;
border-left-width: 0.2rem ;
@@ -408,9 +408,9 @@ code {
padding-left: 0.6rem;
}
.box .box-heading {
- margin: -0.6rem -0.6rem 0 -0.6rem;
- padding: 0.3rem 0 0.3rem 1rem;
- font-weight: 600;
+ margin: 0 -0.6rem;
+ padding-left: 1rem;
+ font-weight: 700;
}
.box .box-heading:not(.box-custom-heading) {
text-transform: capitalize;
@@ -425,17 +425,12 @@ code {
padding: 0.1em;
border-radius: 0.3em;
}
-.box.info{
- border-color: var(--color-blue);
-}
-.box.info .box-heading {
- background-color: var(--color-blue-shadow);
+.box.info, .box.tip{
+ border-color: var(--bs-primary-border-subtle);
+ background-color: var(--bs-primary-bg-subtle);
}
-.box.tip{
- border-color: var(--color-green);
-}
-.box.tip .box-heading {
- background-color: var(--color-green-shadow);
+.box.info .box-heading, .box.tip .box-heading {
+ color: var(--bs-primary-text-emphasis);
}
.box.limitation{
border-color: var(--color-orange);
@@ -444,16 +439,18 @@ code {
background-color: var(--color-orange-shadow);
}
.box.warning{
- border-color: var(--color-yellow);
+ border-color: var(--bs-warning-border-subtle);
+ background-color: var(--bs-warning-bg-subtle);
}
.box.warning .box-heading {
- background-color: var(--color-yellow-shadow);
+ color: var(--bs-warning-text-emphasis);
}
.box.result {
- border-color: rgb(82, 183, 136);
+ border-color: var(--bs-success-border-subtle);
+ background-color: var(--bs-success-bg-subtle);
}
.box.result .box-heading {
- background-color: rgba(82, 183, 136, 0.4);
+ color: var(--bs-success-text-emphasis);
}
.anchor {
display: block;
@@ -602,6 +599,11 @@ a[name] {
background-color: var(--card-background);
}
+.box.benefits .box-heading {
+ color: var(--color-green);
+ font-size: 1.1rem;
+}
+
.box.feature {
margin-top: 1rem;
margin-bottom: 2rem;
@@ -674,4 +676,31 @@ a[name] {
.emoji {
vertical-align: 0.15em;
}
+
+pre.simple {
+ padding: 24px;
+ border-radius: 6px;
+ overflow-x: auto;
+ margin: 0;
+ border: 1px solid #21262d;
+ position: relative;
+}
+pre.simple code {
+ padding: 0;
+}
+pre.simple:has(+ p) {
+ margin-bottom: 12px;
+}
+pre::before {
+ content: attr(data-label);
+ position: absolute;
+ top: 8px;
+ right: 16px;
+ font-size: 11px;
+ color: #6e7681;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ font-family: 'JetBrains Mono', monospace;
+ font-weight: 600;
+}
\ No newline at end of file
diff --git a/webui/src/views/Home.vue b/webui/src/views/Home.vue
index d939bbc6b..e75fcdd14 100644
--- a/webui/src/views/Home.vue
+++ b/webui/src/views/Home.vue
@@ -117,11 +117,28 @@ function showImage(evt: MouseEvent) {
npx go-mokapi https://petstore31.swagger.io/api/v31/openapi.json
+ + Instantly mock Swagger's Petstore API and start testing +
+
@@ -150,7 +166,7 @@ function showImage(evt: MouseEvent) {
- Mokapi helps teams move faster by removing external dependencies from development and testing. + Stop waiting for backends. Stop fighting flaky external APIs. Start shipping with confidence.
Mock HTTP APIs, Kafka topics, LDAP directories, or mail servers - so development never blocks on missing or unstable systems. + so development never blocks on missing or unstable backends.
- Simulate realistic system behavior across protocols - and validate integrations with confidence. + Simulate edge cases, error conditions, and complex scenarios + that are hard or impossible to trigger with live systems.
- Run Mokapi locally, in CI pipelines, or test environments - to automate API testing and speed up feedback loops. + Validate API contracts automatically in CI/CD pipelines, + catching breaking changes before they reach production.
- Mokapi helps teams move quickly without sacrificing confidence or stability. -
-- By mocking and simulating APIs across protocols, you can automate tests, - reduce flaky integrations, and deliver reliable software — even when - external systems are unavailable or evolving. -
-- Mokapi supports multiple protocols, allowing you to test complete systems — + Mokapi supports multiple protocols, allowing you to test complete systems, not just individual REST endpoints.
@@ -288,22 +290,6 @@ function showImage(evt: MouseEvent) {- Mocking APIs across protocols is only the beginning. - Mokapi is designed to help teams prevent bugs, reduce external dependencies, - and create stable development and test environments. -
-- This is made possible through powerful core features — - including JavaScript-based logic, configuration patching, - observability, and realistic data generation. -
-- Run Mokapi in any environment—local development, Docker, cloud, or CI pipelines. Test APIs seamlessly, wherever your services are deployed. + Create dynamic, lifelike data for your mocks. Simulate users, transactions, messages, and more to improve testing accuracy.
- Ensure consistent testing across local development, CI pipelines, and cloud environments. + Produce realistic data to catch bugs early and test edge cases that rarely occur in production.
-
+
- Manage all API mocks, configurations, and behaviors as code. Track changes, simplify audits, and ensure consistency across environments. + Run Mokapi in any environment: local development, Docker, cloud, or CI pipelines. Test APIs seamlessly, wherever your services are deployed.
- Version-controlled mocks reduce errors, simplify audits, and make collaboration easier. + Ensure consistent testing across local development, CI pipelines, and cloud environments.
-
+
- Create dynamic, lifelike data for your mocks. Simulate users, transactions, messages, and more to improve testing accuracy. + Manage all API mocks, configurations, and behaviors as code. Track changes, simplify audits, and ensure consistency across environments.
- Produce lifelike data to catch bugs early and test edge cases that rarely occur in production. + Version-controlled mocks reduce errors, simplify audits, and make collaboration easier.
-
+
Test Kafka producers and consumers by mocking topics according to your AsyncAPI spec. Ensure reliable message generation and integration without a live Kafka cluster.
+Test Kafka producers and consumers by mocking topics according to your AsyncAPI spec. Ensure reliable message generation without a live Kafka cluster.
Start Tutorial @@ -484,7 +469,7 @@ function showImage(evt: MouseEvent) {Step-by-step guide to mock LDAP login using Mokapi and Node.js. Test authentication flows without a real server.
+Step-by-step guide to mock LDAP login using Mokapi and Node.js. Test authentication flows without a real directory server.
Start Tutorial @@ -503,18 +488,28 @@ function showImage(evt: MouseEvent) { - - - -Validate HTTP requests and responses against OpenAPI specs to catch API issues early in development or testing.
- Read Blog +Validate HTTP requests and responses against OpenAPI specs to catch breaking changes early in development or testing.
+ Read Article +Capture real API interactions and replay them in tests. Build test suites from actual production behavior.
+ Read Article